C++: delete this; return x; - c++

Hey I am curious about some C++ behaviour as the code I am working on would benefit greatly from this in terms of simplicity if this behaviour is consistent. Basically the idea is for a specific function inside my object A to compute a complex calculation returning a float, but just before returning the float, occasionally, calling delete this.
1
here is a code example of the functionality i am trying to verify is consistent.
#include <iostream>
#include <stdio.h>
#include <cstdlib>
using namespace std;
struct A
{
float test(float a) {delete this; return a;}
};
int main()
{
A *a = new A();
cout << a->test(1.f) << endl;
cout << "deleted?" << endl;
cout << a->test(1.f) << endl;
}
the output becomes:
1.0
deleted?
*** Error in `./test': double free or corruption (fasttop): 0x0105d008 *** Aborted (core dumped)
I think this means the object was deleted correctly (what is left in memory? an uncallable skeleton of A? A typed pointer? A null pointer?), but am not sure whether I am right about that. If so, is this behaviour going to be consistent (my functions will only be returning native types (floats))
2
Additionally I am curious as to why this doesn't seem to work:
struct A
{
float test(float a) {delete this; return a;}
};
int main()
{
A a;
cout << a.test(1.f) << endl;
}
this compiles but throws the following error before returning anything.
*** Error in `./test': free(): invalid pointer: 0xbe9e4c64 *** Aborted (core dumped)
NOTE Please don't tell reply with a long list of explanations as to why this is bad coding/etiquette or whatever, don't care, I am simply interested in the possibilities.

It is safe for a member function to call delete this; if you know that the object was allocated using scalar new and that nothing else will use the object afterward.
In your first example, after the first call to a->test(1.f), a becomes a "dangling pointer". You invoke Undefined Behavior when you dereference it to call test a second time.
In your second example, the delete this; statement is Undefined Behavior because the object was not created using new.

The behavior is undefined, but in a typical modern implementation the practical "possibilities" of accessing deallocated memory include (but not limited to):
delete releases memory at run-time library level (RTL), but does not return it to the OS. I.e. OS-level memory protection is not engaged and OS continues to see that memory as allocated. However, internal RTL data stored in freed memory blocks clobbers your data. The result: access through the pointer does not cause your code to crash, but the data looks meaningless (clobbered)
Same as 1, but internal RTL data happens not to overlap your critical data. The code does not crash and continues to work "as if" everything is "fine".
delete releases memory to the OS. OS-level memory protection is engaged. Any attempt to access though the pointer causes an immediate crash.
Your examples proceed in accordance with the second scenario, i.e. the data stored in the object appears to remain untouched even after you free the memory.
The crashes you observe in your code happen because RTL detects a double free attempt (or an attempt to free a non-dynamic memory, as in the second example), which is kinda besides the point in the context of your question.

Related

Vector out of boundaries access: why such behavior?

I am aware that out of boundary access of an std::vector in C++ with the operator[] results in undefined behavior. So, I should not expect anything meaningful doing that. However, I'm curious about what is actually happening there under the hood.
Consider the following piece of code:
#include <iostream>
#include <vector>
int main() {
{
std::cerr << "Started\n";
std::vector<int> v(2);
std::cerr << "Successfully initialized vector\n";
v[-1] = 10000; // Note: if accessing v[3], nothing bad seems to happen
std::cerr << "Successfully accessed element -1\n";
}
std::cerr << "Successfully destructed the vector\n";
}
When compiled on GNU/Linux with g++ (GCC) 11.2.0, running this code produces the following output:
Started
Successfully initialized vector
Successfully accessed element -1
double free or corruption (out)
Aborted (core dumped)
Why could have that happened? Why does it cause the destructor to fail? Why does it produce such an error message?
I would understand it if I was using some structure that stored the array together with it on the stack: I would then accidentally access some of its internal data that lies right before v[0] and could have broken something. But as far as I know, the underlying array of std::vector is stored on heap, so the data that I access should not even belong to it, should it? Also, because my last output attempt is taken right after exiting the block with only vector declared in it, I don't see what else except for its destructor could have been called, so the vector seems to be somehow affected by my action...
A hypothetical answer that could have happened: The UB caused arbitrary piece of memory to be overwritten. This is called memory corruption.
That overwritten arbitrary piece of memory happened to be right before the dynamic memory that the vector allocated. The arbitrary piece of memory right before the allocation happened to contain an "information header" that describes the allocation. When the destructor was called, there was an attempt to deallocate the memory. The global allocator detected that the corrupted information was inconsistent, produced the diagnostic message and terminated the program.
This is what the source code of the global memory allocator on your system may look like: https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#4326 The link leads specifically to the line that produces the error message.

How variable 'a' will exist without any object creation?

How variable int a is in existence without object creation? It is not of static type also.
#include <iostream>
using namespace std;
class Data
{
public:
int a;
void print() { cout << "a is " << a << endl; }
};
int main()
{
Data *cp;
int Data::*ptr = &Data::a;
cp->*ptr = 5;
cp->print();
}
Your code shows some undefined behavior, let's go through it:
Data *cp;
Creates a pointer on the stack, though, does not initialize it. On it's own not a problem, though, it should be initialized at some point. Right now, it can contain 0x0badc0de for all we know.
int Data::*ptr=&Data::a;
Nothing wrong with this, it simply creates a pointer to a member.
cp->*ptr=5;
Very dangerous code, you are now using cp without it being initialized. In the best case, this crashes your program. You are now assigning 5 to the memory pointed to by cp. As this was not initialized, you are writing somewhere in the memory space. This can include in the best case: memory you don't own, memory without write access. In both cases, your program can crash. In the worst case, this actually writes to memory that you do own, resulting in corruption of data.
cp->print();
Less dangerous, still undefined, so will read the memory. If you reach this statement, the memory is most likely allocated to your program and this will print 5.
It becomes worse
This program might actually just work, you might be able to execute it because your compiler has optimized it. It noticed you did a write, followed by a read, after which the memory is ignored. So, it could actually optimize your program to: cout << "a is "<< 5 <<endl;, which is totally defined.
So if this actually, for some unknown reason, would work, you have a bug in your program which in time will corrupt or crash your program.
Please write the following instead:
int main()
{
int stackStorage = 0;
Data *cp = &stackStorage;
int Data::*ptr=&Data::a;
cp->*ptr=5;
cp->print();
}
I'd like to add a bit more on the types used in this example.
int Data::*ptr=&Data::a;
For me, ptr is a pointer to int member of Data. Data::a is not an instance, so the address operator returns the offset of a in Data, typically 0.
cp->*ptr=5;
This dereferences cp, a pointer to Data, and applies the offset stored in ptr, namely 0, i.e., a;
So the two lines
int Data::*ptr=&Data::a;
cp->*ptr=5;
are just an obfuscated way of writing
cp->a = 5;

Is alocating specific memory for a void pointer undefined behaviour?

I've met a situation that I think it is undefined behavior: there is a structure that has some member and one of them is a void pointer (it is not my code and it is not public, I suppose the void pointer is to make it more generic). At some point to this pointer is allocated some char memory:
void fooTest(ThatStructure * someStrPtr) {
try {
someStrPtr->voidPointer = new char[someStrPtr->someVal + someStrPtr->someOtherVal];
} catch (std::bad_alloc$ ba) {
std::cerr << ba.what << std::endl;
}
// ...
and at some point it crashes at the allocation part (operator new) with Segmentation fault (a few times it works, there are more calls of this function, more cases). I've seen this in debug.
I also know that on Windows (my machine is using Linux) there is also a Segmentation fault at the beginning (I suppose that in the first call of the function that allocates the memory).
More, if I added a print of the values :
std::cout << someStrPtr->someVal << " " << someStrPtr->someOtherVal << std::endl;
before the try block, it runs through the end. This print I've done to see if there is some other problem regarding the structure pointer, but the values are printed and not 0 or negative.
I've seen these topics: topic1, topic2, topic3 and I am thinking that there is some UB linked to the void pointer. Can anyone help me in pointing the issue here so I can solve it, thanks?
No, that in itself is not undefined behavior. In general, when code "crashes at the allocation part", it's because something earlier messed up the heap, typically by writing past one end of an allocated block or releasing the same block more than once. In short: the bug isn't in this code.
A void pointer is a perfectly fine thing to do in C/C++ and you can usually cast from/to other types
When you get a seg-fault while initialization, this means some of the used parameters are themselves invalid or so:
Is someStrPtr valid?
is someStrPtr->someVal and someStrPtr->someotherVal valid?
Are the values printed is what you were expecting?
Also if this is a multuthreaded application, make sure that no other thread is accessing those variables (especially between your print and initialization statement). This is what is really difficult to catch

C++ stack and heap corruption

I was recently reading about stack & heap corruption in C & C++. The author of the website demonstrates stack corruption using below example.
#include<stdio.h>
int main(void)
{
int b = 10;
int a[3];
a[0] = 1;
a[1] = 2;
a[2] = 3;
printf(" b = %d \n",b);
a[3] = 12; // oops it is invalid, behaviour is undefined
printf(" b = %d \n",b);
printf("address of b= %x\n",&b);
printf("address of a[3]= %x\n",&a[3]);
return 0;
}
I tested above program on visual studio 2010 compiler (VC++) & it gives me runtime error that says:
stack around variable a gets corrupted
Now my question: is stack corrupted for lifetime or it is only for the time during when above erroneous program was being executed?
Same way, I know that deleting same pointer twice might do really bad things like heap corruption.
The following code:
int* p=new int();
delete p;
delete p; // oops disaster here, undefined behaviour
When the above code fragment executes the VC++ shows heap corruption error at runtime.
It is Undefined Behaviour. You cannot know what will happen if you do 'forbidden' things. You have no guarantee that your program will work well.
You have to be careful with terminology here. Will the stack be "corrupted" for the remainder of your program's life? It may be; it may not be. In this instance you've only corrupted data within the current stack frame, so once you're out of that function call, in practice your "corruption" will have gone.
But that's not quite the whole story. Since you've overwritten a variable with bytes that aren't supposed to be there, what knock-on effects might that have on your program? The consequences of this memory corruption could feasibly be logically passed on to other function scopes, or even other computers if you're sending this data over a network connection and the data is no longer in the expected form. (Typically, your data protocol will have safety features built into it to detect and discard unexpected forms of data; but, that's up to you.)
The same is true of heap corruption. Any time you overwrite the bytes of something that is not supposed to be overwritten, and any time you do so with arbitrary or unknowable data, you run the risk of potentially catastrophic consequences that may logically last well beyond the lifetime of your program.
Within the scope of C++ as a language, this condition is summed up in a specific phrase: undefined behaviour. It states that you can't really rely on anything at all after you've corrupted your memory. Once you've invoked UB, all bets are off.
The one guarantee that you usually have in practice is that your OS will not allow you to directly overwrite any memory that does not belong to your program. That is, corrupting the memory of other processes or of the OS itself is very difficult. The memory model of modern OSs is deliberately designed that way in order to keep programs isolated and prevent this kind of damage from broken programs and/or viruses.
C++ as well as C does not have array boundary overflow or underflow check. However, you can abstract out, you may define an array with overloaded index operator (operator []) where you can check for array index out of bounds and act accordingly. When you delete a pointer using delete ptr (when ptr is allocated through new), the space allocated before is returned back to heap space, however the value of the pointer becomes same as before. So, it;s a good programming practice that you should make the ptr NULL after delete, e.g.
int* p=new int();
...
if (p) {
delete p;
p = (int *) NULL;
}
// double deletion is prevented, and ptr us not dangling any more
if (p) {
delete p;
p = (int *) NULL;
}
However, stack or heap corruption, if at all, lies confined within the program space and when the program terminates, normally or abnormally, all memory occupies are released back to the operating system

The meaning of static in C++

I thought I was fairly good with C++, it turns out that I'm not. A previous question I asked: C++ const lvalue references had the following code in one of the answers:
#include <iostream>
using namespace std;
int& GenX(bool reset)
{
static int* x = new int;
*x = 100;
if (reset)
{
delete x;
x = new int;
*x = 200;
}
return *x;
}
class YStore
{
public:
YStore(int& x);
int& getX() { return my_x; }
private:
int& my_x;
};
YStore::YStore(int& x)
: my_x(x)
{
}
int main()
{
YStore Y(GenX(false));
cout << "X: " << Y.getX() << endl;
GenX(true); // side-effect in Y
cout << "X: " << Y.getX() << endl;
return 0;
}
The above code outputs X: 100, X:200. I do not understand why.
I played with it a bit, and added some more output, namely, a cout before the delete x; and a cout after the new x; within the reset control block.
What I got was:
before delete: 0x92ee018
after new: 0x92ee018
So, I figured that static was silently failing the update to x, and the second getX was playing with (after the delete) uninitialized memory; To test this, I added a x = 0; after the delete, before the new, and another cout to ensure that x was indeed reset to 0. It was.
So, what is going on here? How come the new returns the exact same block of memory that the previous delete supposedly free'd? Is this just because that's what the OS's memory manager decided to do, or is there something special about static that I'm missing?
Thank you!
That's just what the memory manager decided to do. If you think about it, it makes a lot of sense: You just freed an int, then you ask for an int again... why shouldn't the memory manager give you back the int you just freed?
More technically, what is probably happening when you delete is that the memory manager is appending the memory block you freed to the beginning of the free list. Then when you call new, the memory manager goes scanning through its free list and finds a suitably sized block at the very first entry.
For more information about dynamic memory allocation, see "Inside storage allocation".
To your first question:
X: 100, X:200. I do not understand why.
Since Y.my_x is just a reference to the static *x in GenX, this is exactly how it supposed it to be - both are referencing to the same address in memory, and when you change the content of *x, you get a side effect.
You are accessing the memory block that is deallocated. By the c++ standard, that is an undefined behaviour, therefore anything can happen.
EDIT
I guess I have to draw :
You allocate the memory for an int, and you pass the object allocated on the heap to the constructor of Y, which stores that in the reference
then you deallocate that memory, but your object Y still holds the reference to the deallocated object
then you access the Y object again, which holds an invalid reference, referencing deallocated object, and the result you got is the result of an undefined behaviour.
EDIT2
The answer to why : implementation defined. The compiler can create new object at any location it likes.
i test your code in VC2008, the output is X: 100, X: -17221323. I think the reason is that the static x is freed. i think my test is reasonable.
This makes perfect sense for the code.
Remember that it is your pointer that is static, so when you enter this function a second time you do not need to make a new pointer, but ever time you enter this function you are making a new int for the pointer to point to.
You are also probably in debug mode where a bit more time is spent giving you nice addresses.
Exactly why the int that your pointer points to is in the same space is probably just down to pure luck, that and you are not declaring any other variables before it so the same space in memory is still free