views:

382

answers:

4

I've tried to build a very minimalistic memory read library to read some unsigned ints out of it. However, I run into a "HEAP CORRUPTION DETECTED" error message when the ReadUnsignedInt method wants to return.

HEAP CORRUPTION DETECTED. CRT detected that the application wrote to memory after end of buffer.

As I have read, this may be the cause when trying to double delete something. This may be caused by some incorrect usage of the std::tr1::shared_ptr but I cannot determine what I am doing wrong with them. Code is as follows (error handling omitted):

unsigned int Memory::ReadUnsignedInt (unsigned int address) const {
    std::tr1::shared_ptr<byte> bytes = 
        this->ReadBytes(address, sizeof(unsigned int));
    return *((int*)bytes.get());
    // correct value (how to improve this ugly piece of code?)
}

std::tr1::shared_ptr<byte> Memory::ReadBytes (
    unsigned int address, int numberOfBytes) const
{
    std::tr1::shared_ptr<byte> pBuffer(new byte(numberOfBytes));
    ReadProcessMemory(m_hProcess.get(), (LPCVOID)address, 
        pBuffer.get(), numberOfBytes * sizeof(byte), NULL))
    return pBuffer;
}
+2  A: 

The issue is:

new byte(numberOfBytes)

This allocates a single byte with a value of numberOfBytes.

You want to do:

new byte[numberOfBytes]

Which allocates an array of bytes numberOfBytes long.

But since you know you are only reading 4 bytes, why bother allocating memory at all? Just pass the address of a unsigned int on the stack.

Michael
A: 

I believe new byte(numberOfBytes) should be new byte[numberOfBytes]. Otherwise you would have allocated only one byte. Just to complete the answer, as @ephemient indicated you can not use shared_ptr here as it will do a delete where as you should do delete[]. If not done like this, the behavior will be undefined.

Naveen
+2  A: 

Michael and Naveen have both found the same major flaw in your code, but not the only flaw.

shared_ptr will delete the pointed-at object when its reference count goes to zero.

This means you can only give it objects allocated by new -- not new[].

You may wish to use shared_ptr<vector<byte> > or boost::shared_array<byte> instead.

ephemient
Excellent catch - although this would be a silent error on most platforms since byte doesn't have a destructor that needs to be called, the correct answer here would be to use vector instead of shared_ptr.
Michael
...or shared_array for the matter (I think).
peterchen
Updated. I was investigating how to add a custom destructor to `shared_ptr`, but it's more effort than it's worth.
ephemient
+2  A: 

The basic problems with your code have been pointed out already. Looking at it, I'm left wondering why you'd use a shared_ptr here at all, though. If I were doing it, I'd probably use something like this:

unsigned Memory::ReadUnsignedInt(unsigned address) { 
    unsigned ret;
    ReadProcessMemory(m_hProcess.get(), (void *)address, &ret, sizeof(ret), NULL);
    return ret;
}

std::vector<char> Memory::ReadBytes(unsigned address, int num) { 
    std::vector<char> ret(num);
    ReadProcessMemory(m_hProcess.get(), (void *)address, &ret[0], num, NULL);
    return ret;
}

Then again, instead of ReadUnsignedInt, I'd be tempted to use a template:

template <class T>
T Memory::Read(unsigned address) { 
    T ret;
    ReadProcessMemory(m_hProcess.get(), (void*)address, &ret, sizeof(ret), NULL);
    return ret;
}

Since you don't pass a parameter from which it can deduce the type for the template parameter, you'd always have to specify explicitly:

int x = Read<int>(wherever);
char a = Read<char>(wherever);

The alternative would be to pass the destination as a parameter:

template <class T>
Memory::Read(unsigned address, T &t) { 
    ReadProcessMemory(my_hProcess.get(), (void *)address, &t, sizeof(t), NULL);
};

which you'd use like:

Read(wherever, some_int);
Read(somewhere, some_long);

and so on.

If you're worried about the inefficiency of returning a vector of char, you probably shouldn't -- VC++ (like most other reasonably current compilers) has what's called a "named return value optimization", which means in a situation like this, it passes a hidden reference to the vector you assign the result to, and ReadBytes will use that to deposit the data directly where it's going to end up anyway. For that matter, with any reasonable optimization turned on at all, ReadBytes will almost certainly end up as an inline function, so nothing involved really gets "passed" or "returned" at all.

On the other hand, this code won't work particularly well with older compilers -- and with old enough compilers, the versions using member template functions probably won't even compile. As long as you use a reasonably current compiler, however, life should be good.

Jerry Coffin