Indexing to a function, signal or property with the C __index metamethod of Instances breaks on long strings unless it is in camelCase

Because Instances in Roblox are just userdatas with metamethods that act as a Lua wrapper to interact with the C++ API, I would expect to directly interact with the metamethod functions and expect no issues.

--> I am experimenting around the metamethods of Instances to create a way to combat against DataModel sandboxing. It is a technique used by reverse engineers to try and gain more information about what an obfuscated script is doing by changing/hooking the metamethods of DataModel and logging whenever the metamethods get invoked.

local __index = select(2, xpcall(function()
	return game.SomethingThatGetsTheMetamethodToError
end, function()
	return debug.info(2, "f") --> returns the __index metamethod of Instances
end))

print(__index(game, "IsLoaded")) --> Successfully indexes into the .IsLoaded function

Using the __index metamethod to index into something like the game.IsLoaded function will work perfectly fine like this __index(game, "IsLoaded") --> function: 0x9aca4a431195d879. If I put a NULL terminator at the end of the string, everything after it will be ignored, as NULL characters are used to declare the end of a C-style string. This example below still works perfectly fine:

__index(game, "IsLoaded\000x") --> function: 0x9aca4a431195d879

However, a weird error arises when I pass a long string, like in the example below:

__index(game, "IsLoaded\000" .. string.rep("random", 150))

--> Throws error `IsLoaded is not a valid member of DataModel "Place1"`

{6E416FD3-00AE-4765-AE8B-98AA7346658D}

This error can be solved by… Indexing into the .IsLoaded function in camelCase.

__index(game, "isLoaded\000" .. string.rep("random", 150)) --> function: 0x9aca4a431195d879

This issue only arises when attempting to index into a function, property or an RBXScriptSignal. It does not occur when indexing into the children of the Instance.

Expected behavior

What is expected to happen:
Even when passing lengthy strings to the __index metamethod function, I will still be able to index into functions, properties, and signals without having to index into it in camelCase.

5 Likes

My question is how did you manage to find this bug afterall.

2 Likes

I added a comment about that in the first code snippet above!

2 Likes

Thank you for the report.

Your expected behavior is incorrect.
If we do change this, the only logical thing will be to make only “IsLoading” work as any longer strings with embedded 0 are not the same.
So expect your “working” example to break.

I’m just curious, why does the camelCase variant work?

The code below works, the behaviour of only camelCase variants working is only present when the length of the string goes over the unsigned 8-bit integer limit, or 256 and higher.

print(__index(game, "IsLoaded\000\000\000123456789HiThere"))