I have a pretty nasty bug that has been bothering for a while. Here's the situation, I'm creating an in memory filesystem. I have pre-allocated data blocks for each file in which to do reads and writes from. To implement directories I have a simple std::vector object containing all the files in the directory. This vector object is at the top of the file in each directory. Therefore, in order to read and write from the directory I read the first 16 bytes into a character buffer and type cast it as a vector (16 bytes because sizeof(vector<T>)
is 16 on my system). Specifically, the first 16 bytes are not elements of the vector, but the vector itself. However, the vector is somehow being clobbered after I exit out of a key function.
The following code does not throw an exception and can correctly save a vector to a character buffer to be later retrieved.
#include <vector>
char dblock[16];
typedef std::vector<int> Entries;
void foo() {
char buf[sizeof(Entries)];
Entries* test = new (buf)Entries();
test->push_back(0);
for (int i = 0; i < sizeof(std::vector<int>); ++i) {
dblock[i] = buf[i];
}
}
void bar() {
char buf[sizeof(Entries)];
for (int i = 0; i < sizeof(std::vector<int>); ++i) {
buf[i] = dblock[i];
}
Entries* test = (Entries*)buf;
test->back();
}
int main()
{
foo();
bar();
return 0;
}
However, once the function foo is changed to include an argument like so, any time I try to use iterators an exception is thrown.
void foo(int this_arg_breaks_everything) {
char buf[sizeof(Entries)];
Entries* test = new (buf)Entries();
test->push_back(0);
for (int i = 0; i < sizeof(std::vector<int>); ++i) {
dblock[i] = buf[i];
}
}
Looking at the disassembly, I found the problem assmbler when the function is tearing down its frame stack:
add esp,128h <----- After stack is reduced, vector goes to an unusable state.
cmp ebp,esp
call __RTC_CheckEsp (0D912BCh)
mov esp,ebp
pop ebp
ret
I found this code by using the debugger to test if ((Entries*)dblock)->back() returns an exception or not. Specifically the exception is "Access violation reading location 0XCCCCCCCC". The location in question is std::vector's std::_Container_base12::_Myproxy->_Mycont->_Myproxy == 0XCCCCCCCC; you can find it at line 165 of xutility.
And yes, the bug only happens when the inplace new operator is used. Using a normal new operator then writing test to dblock doesn't throw an exception.
Thus, I came to the conclusion that the ultimate reason for this is somehow the compiler is doing a bad stack unwind clobbering some part of memory that it shouldn't be.
Edit: Changed wording for clarity sake. Edit2: Explained magic numbers.