Pavel
But visual code and code blocks dont
Pavel
Why do I call destructor R[i].~ClassRoom(); but the program still outputs the rooms? R: Room: 1 7 1 Room: 2 4 4 Room: 3 9 1 Room: 4 2 4 Output sequence is being processed after calling the destructor
As Vlad mentioned, you shouldn't call destructor explicitly here. Also calling destructor doesn't mean that something will be done with the memory of the object (nulling variables/memory involves extra operations, it is not needed since there's no object at that memory anymore, so not to pay extra cost it is likely that nothing is going to be done to the memory), and especially nothing going to be done in regards to your array, since destructor only cares about the object. You can read more about how "new" and "delete" work (just google articles). And if you need a dynamic array where you can move elements around and change its size, use std::vector
Anonymous
also destructors are mainly used to free any allocations made in the constructor or lifetime of the object
Anonymous
mainly RAII
Vlad
because the destructor is not erasing its data
You can't "erase" the data ever. Even if you delete on the pointer, you could still access it afterwards. Albeit that it causes UB obviously
Anonymous
yea
Anonymous
and technically you can be filling it with zero
Vlad
If you store by value best you can do is to set up a flag of some kind that it's in fact empty
Anonymous
~m() { // erase contents incase someone posses our pointer memset(rooms, 0, room_len); // erase our pointer incase someone trues to use us rooms = nullptr; }
Anonymous
assuming rooms itself is not allocated
Vlad
assuming rooms itself is not allocated
Then you cant set it to nullptr :)
Anonymous
otherwise ~m() { // erase contents incase someone posses our pointer memset(rooms, 0, room_len); // erase our pointer incase someone trues to use us delete rooms; rooms = nullptr; }
Vlad
Not like you're gonna access it upon deletion or something
Vlad
And after free anything could happen to a buffer
Anonymous
Then you cant set it to nullptr :)
technically it can since it may simply be a stack variable private: int rooms_internal[]; public: int * rooms;
Anonymous
And after free anything could happen to a buffer
still, it is important if it is security is of concern
Anonymous
eg an attacker/malisious lib/program may try to read contents of memory after it has been freed
Vlad
still, it is important if it is security is of concern
Then i'd fill it up with a pattern of some sort
Vlad
So it'd be recognizable
Anonymous
memset 0 is sufficent
Anonymous
since the pattern COULD be decoded eventually, compromizing your data
Vlad
If it's 0xFDFDFDFD or smth
Anonymous
(tho in that case, simply attempting to read data WHILE your class is active is enough to compromise it as any such memory read attack would be)
Alviro Iskandar
otherwise ~m() { // erase contents incase someone posses our pointer memset(rooms, 0, room_len); // erase our pointer incase someone trues to use us delete rooms; rooms = nullptr; }
it doesn't guarantee the pointer you memset gets zeroed the compiler is allowed to omit the memset() if it can prove that the pointer is going to be deleted right after memset
Alviro Iskandar
you need a memory barrier
Anonymous
true
Anonymous
tons of valuable info could be gained if kernel memory could be read via an exploit by an attacker
Anonymous
and they where persistant enough and knew what to look for
Vlad
I'm pretty sure that is a headache of the OS devs
Anonymous
:)
Vlad
What to do with memory after free
Vlad
So it wouldn't be recognizable
Alviro Iskandar
in the linux kernel we have kfree_sensitive() to zero the memory before free
Anonymous
you might be able to use a custom deleter to memset it, or a hardened malloc/free/new/delete implementation specifically designed for such
Alviro Iskandar
So it wouldn't be recognizable
in case you have a vulnerability, the attacker won't get a chance to read your secret credential
Alviro Iskandar
like private key in memory, etc.
Alviro Iskandar
freeing doesn't always clobber the content, so it may reside in memory
Alviro Iskandar
https://github.com/torvalds/linux/blob/v5.18/mm/slab_common.c#L1234-L1255
Anonymous
such as the one used in GraphineOS
Anonymous
https://github.com/GrapheneOS/hardened_malloc i think it is
Anonymous
CONFIG_ZERO_ON_FREE: true (default) or false to control whether small allocations are zeroed on free, to mitigate use-after-free and uninitialized use vulnerabilities along with purging lots of potentially sensitive data from the process as soon as possible. This has a performance cost scaling to the size of the allocation, which is usually acceptable. This is not relevant to large allocations because the pages are given back to the kernel.
Pavel
If you store by value best you can do is to set up a flag of some kind that it's in fact empty
Compiler is free to not generate assembly for this code however, if it doesn't change the behavior of the destructor itself
Anonymous
also https://github.com/GrapheneOS/hardened_malloc#security-properties
Pavel
I think even if it changes the behaviour it can skip writing to the memory (if it stored value in a register), but not sure if this really can happen
Anonymous
anyway
Anonymous
how do i detect overflow in a subtraction operation? eg public int Add(int x, int y) { int r = x + y; int s = (x >> 31); fOK &= s != (y >> 31) || s == (r >> 31); return r; } public long Add(long x, long y) { long r = x + y; long s = (x >> 63); fOK &= s != (y >> 63) || s == (r >> 63); return r; } public uint Add(uint x, uint y) { uint r = x + y; fOK &= (r >= x); return r; } public ulong Add(ulong x, ulong y) { ulong r = x + y; fOK &= r >= x; return r; }
Vlad
After the destructor is called the object is toast
Vlad
You can't access it anyway
Vlad
Latter is obviously not portable
Alviro Iskandar
Either use bigger type and cast down, or check flags in assembly
yeah, the most efficient way is checking the OF (overflow flag)
Anonymous
without using any builtins or assembly or casting
Anonymous
as for example public long Add(long x, long y) { long r = x + y; long s = (x >> 63); fOK &= s != (y >> 63) || s == (r >> 63); return r; } this should be reversable/flippable for a Sub equivilant, right?
Vlad
without using any builtins or assembly or casting
int64_t ok = a + b; return ok > INT_MAX;
Vlad
But it would only work if both are positive
Anonymous
int64_t ok = a + b; return ok > INT_MAX;
that doesnt work for int64_t itself, right?
Anonymous
eg int64_t + int64_t
Vlad
I guess you can check int64_t through a double variable
Vlad
Hence it's range is bigger
Vlad
But there might be accuracy issues idk
Anonymous
also wont be as fast
Anonymous
depending on hardware
Vlad
also wont be as fast
On x86 not really
Vlad
FPU pipeline works almost concurrently
Anonymous
eg, for int64_t + int64_t we do // assuming sizeof(long) == sizeof(int64_t) long r = x + y; long s = (x >> 63); fOK &= s != (y >> 63) || s == (r >> 63); // fOK = !overflowed
Anonymous
and for unsigned equivilant we simply do ulong r = x + y; fOK &= r >= x;
Anonymous
hmmm i wonder if this would fail if x and y where flipped