Gio Borje
Gio Borje

Reputation: 21134

Access violation C++ (Deleting items in a vector)

I'm trying to remove non-matching results from a memory scanner I'm writing in C++ as practice. When the memory is initially scanned, all results are stored into the _results vector.

Later, the _results are scanned again and should erase items that no longer match.

The error:

Unhandled exception at 0x004016f4 in .exe: 0xC0000005: Access violation reading location 0x0090c000.

// Receives data

DWORD buffer;

for (vector<memblock>::iterator it = MemoryScanner::_results.begin(); it != MemoryScanner::_results.end(); ++it) {
    // Reads data from an area of memory into buffer
    ReadProcessMemory(MemoryScanner::_hProc, (LPVOID)(*it).address, &buffer, sizeof(buffer), NULL);

    if (value != buffer) {
        MemoryScanner::_results.erase(it); // where the program breaks
    }
}

Upvotes: 0

Views: 2053

Answers (3)

In silico
In silico

Reputation: 52207

Erasing elements from a std::vector<T> will invalidate the it iterator since the std::vector<T> object will shift elements around to keep the underlying array contiguous after deleting an item.

Fortunately, vector<T>::erase() returns a new, valid iterator so that you don't end up trying to dereference an invalid iterator:

DWORD buffer;
vector<memblock>::iterator it = MemoryScanner::_results.begin(); 
while(it != MemoryScanner::_results.end())
{
    ReadProcessMemory(MemoryScanner::_hProc, (LPVOID)(*it).address,
        &buffer, sizeof(buffer), NULL);
    if (value != buffer)
    {
        it = MemoryScanner::_results.erase(it);
    }
    else
    {
        ++it;
    }
}

Another way to delete items in a vector

Have you considered using the erase-remove idiom?

struct RemoveNonMatches
{
public:
    RemoveNonMatches(HANDLE p, DWORD v) : proc(p) val(v) {}

    bool operator()(const memblock& obj)
    {
        DWORD buffer;
        ReadProcessMemory(proc, static_cast<LPVOID>(obj.address),
            &buffer, sizeof(buffer), NULL);
        return (buffer != val);
    }

private:
    HANDLE proc
    DWORD val;
};

// ...

MemoryScanner::_results.erase
(
    std::remove_if
    (
        MemoryScanner::_results.begin(),
        MemoryScanner::_results.end(),
        RemoveNonMatches(MemoryScanner::_hProc, value)
    ),
    MemoryScanner::_results.end()
);

Upvotes: 5

Karl Knechtel
Karl Knechtel

Reputation: 61643

The std::remove_if version, for completeness:

bool has_bad_memory_contents(const memblock& block) {
    DWORD buffer;
    ReadProcessMemory(
        MemoryScanner::_hProc, (LPVOID)block.address,
        &buffer, sizeof(buffer), NULL
    );
    return buffer != value;
}

std::vector<memblock>& r = MemoryScanner::_results;
r.erase(std::remove_if(r.begin(), r.end(), has_bad_memory_contents), r.end());

Upvotes: 0

Alex Budovski
Alex Budovski

Reputation: 18456

vector::erase invalidates the iterator you passed to erase(). You then attempt to increment it. UB ensues.

Upvotes: 1

Related Questions