Veecy
Veecy

Reputation: 73

Ignoring array out-of-bounds error in GML

I'm sure this is a super weird question, but believe me, it has a point. I have an array of values in GameMaker Language, and I want to be able to search the array for values outside of the array's bounds, and retrieve those out-of-bounds values rather than returning an error and stopping the game. Basically, I want to intentionally pull incorrect data from nearby memory when searching the array with an out-of-bounds index. Is this possible? Thank you in advance!

Upvotes: 0

Views: 406

Answers (1)

YellowAfterlife
YellowAfterlife

Reputation: 3202

but believe me, it has a point

It is good to mention why you want to do something so that if that cannot be done, recommendations can be given without follow-up question(s).

GameMaker does not allow invalid memory access (save for exotic bugs), though even if it did, there would be more to this - if you inspect YYGML.h (in the runtime directory), GML values are represented by struct RValue, which is a 16-byte structure consisting of 8 value bytes (such as a numeric value, a string pointer, an array pointer, or so on), value type index, and metadata flags. In other words, attempting to read arbitrary memory as a GML value would not work since both type and flags would be garbage, most likely causing the runtime to crash with an access violation the moment you hit a pointer-typed value pointing at an invalid memory location.

If you would like to read arbitrary memory within your process, you could write a native extension (usually in C++) with a function that takes a buffer address (from buffer_get_address), starting address, and length, copying the bytes to the buffer by calling VirtualQuery (to validate that memory region is accessible and retrieve bounds) and subsequently memcpy.

A non-GML specific implementation of such a function may look like this:

bool safeCopy(byte* out, const byte* from, size_t size) {
    const auto pmask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE |
        PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
    auto at = from;
    auto till = at + size;
    ::MEMORY_BASIC_INFORMATION mbi{};
    while (at < till) {
        if (!VirtualQuery(at, &mbi, sizeof mbi)) return false;
        if ((mbi.State == MEM_COMMIT) && !(mbi.Protect & PAGE_GUARD) && (mbi.Protect & pmask)) {
            const byte* m_begin = static_cast<const byte*>(mbi.BaseAddress);
            const byte* m_end = m_begin + mbi.RegionSize;
            at = m_end;
            if (m_end > till) m_end = till;
            if (m_begin < from) m_begin = from;
            memcpy(out + (m_begin - from), m_begin, m_end - m_begin);
        } else return false;
    }
    return true;
}

Upvotes: 1

Related Questions