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.

9 Likes

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

3 Likes

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

4 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"))

image
Looks like they adjusted this behavior and will no longer allow extra bytes, which is unfortunate for anti-cheat development.

5 Likes

It’s part of Roblox’s reverse-compatibility layers I believe

I find it extremely funny yet frustrating how Roblox decides to patch every “good” method related to Lua Bridge to supposedly “remove old API behavior” and “clean up the codebase”. I do not get why this is a priority considering previous Roblox client source leaks were full of bug riddled code, notably the usage of (void*)NULL instead of nullptr. So there’s definitely better things to change.

They don’t actually care about real improvements, their happiness comes from breaking every single feature that does NOT impact actual gameplay at all

5 Likes

Funny thing about the new update is that this only applies to indexing with the game metatable, but for using null bytes as arguments in functions like collectgarbage for example, or using null bytes to index with the Enum metatable, it does not have the same warning.


image
If you are really, truly going to implement this change so that extra (null) bytes error, you might as well have a little consistency. Or is there a specific reason why it applies to the game metatable only? :thinking: :thinking:

2 Likes

Enums are tables that have metamethods. With the same debug.info method used in the first post, you can also get the __index metamethod of Enum.

local __index = select(2, xpcall(function()
	return Enum.Status.x
end, function()
	return debug.info(2, "f")
end))

print(__index)
1 Like

Once again, thank you for bringing our attention to this as we were not aware of this specific problem before.

Because of a very widespread misuse of the object field access, we have decided to add a warning to highlight the problems in the code with plans to convert it into an error in the future.
For backwards compatibility reasons, it will become an error in Studio first, followed by Servers and then at a more distant date, the Client as well (depending on velocity of developers making fixes).

What do you define as misuse of the field

3 Likes

This. Is the “misuse” perhaps correlated with anti-cheat development :thinking: @WheretIB ? All it does, from what I’m seeing, is benefit developers.

2 Likes

And are you going to list a schedule for when these changes will take place


Seems like we won’t be getting an answer any time soon - it almost seems as if they are ghosting us!

3 Likes