Why disable the debug lib without warning?

Why would you remove the ability to use debug.setupvalue on the client??? I get its a debug library but even FiveM uses it in their standard scripts and scheduler. Even if this is for the sake of stopping ‘some’ mod menus this hurts dev more than it most likely will help. What’s next stopping the use of getinfo or setloal or upvaluejoin, at this point just remove the debug lib on the client entirely so devs don’t have to constantly change code whenever the sources changes.

This needs to be reverted or an official stance made so devs don’t use things that are not supported: feat(citizen-scripting-lua): sandboxed os, io and debug library · citizenfx/fivem@db5d0ad · GitHub

Seems like only these 5 methods are left to be used now

Client Dump of debug lib

{
["getupvalue"] = function: 00007FFB7D9403B0,
["setmetatable"] = function: 00007FFB7D93F600,
["getinfo"] = function: 00007FFB7D93F730,
["traceback"] = function: 00007FFB7D9404C0,
["getmetatable"] = function: 00007FFB7D93F550,
}

Server Dump of debug lib

{
  ["traceback"] = function: 00007FFBBF38D750,
  ["debug"] = function: 00007FFBBF38D320,
  ["setupvalue"] = function: 00007FFBBF38C6B0,
  ["upvalueid"] = function: 00007FFBBF38C770,
  ["gethook"] = function: 00007FFBBF38D100,
  ["getregistry"] = function: 00007FFBBF38AF80,
  ["getinfo"] = function: 00007FFBBF38B320,
  ["setlocal"] = function: 00007FFBBF38C250,
  ["sethook"] = function: 00007FFBBF38CAF0,
  ["getuservalue"] = function: 00007FFBBF38B0E0,
  ["getupvalue"] = function: 00007FFBBF38C6A0,
  ["getmetatable"] = function: 00007FFBBF38AFB0,
  ["upvaluejoin"] = function: 00007FFBBF38C810,
  ["setuservalue"] = function: 00007FFBBF38B140,
  ["getlocal"] = function: 00007FFBBF38BFA0,
  ["setmetatable"] = function: 00007FFBBF38B060,
}

Upvalues at the least should stay when we can access the ENV of functions already with getupvalue, setting upvalues are basically the same as just editing the _ENV table, same with upvaluejoin.

These changes were introduced in an effort to sandbox the server more, so the server debug library will have the same functions after updating. These changes were announced last month in the experiments group.

The widely used functions such as setmetatable and getinfo still exist, what is your use-case for other methods in the debug library?

Thank you for the response I guess I missed the part of the upvalues being removed from debug. If widely used functions are staying that fine but I still would want the full functionality of the debug lib on the client (at least upvalues).

There are a few use cases for upvalues as I have used them so far.

  1. being able to update variables (global AND local) outside of scope or stack lock. I was finally able to get a working variable updater without a callback by utilizing the upvalues. This might not seem like it would be helpful but creating a framework and a function to do this helps devs down the line.
-- File 1
local weaponlist = {}

Citizen.CreateThread(function() -- Needs to be in a thread or a function block with the first upvalue being the fucker changed
	weaponlist = {} -- value needs to be an upvalue of the function (fuck lua for this but still the best pl there is)
	ExecuteVariableChanger('xinv:getSharedTable', 2, _ENV, "weaponlist", "weaponNames")() -- this will get the value and update the variable
end)

-- File 2

ExecuteVariableChanger = function(typeof, level, env, VarName, ...)
		if not debug.setupvalue or not debug.upvaluejoin then return false end -- fuck fivem new change
		local x -- fuck you lua and stacks and debug lib
		local finder = setmetatable({__env = env, __level = level, __cb = debug.getinfo(level).func}, {
			__index = function(a,b)
				--print('i', a,b)
				local val = a.__env[b]
				if val == nil then
					-- look through locals
					local i = 1
					while true do
						local name, value = debug.getlocal(a.__level, i)
						if not name then break end
						if name == b then
							return value
						end
						i = i + 1
					end
				end
				if val == nil and rawget(a, "__cb") then
					-- use upvalues
					local i = 1
					while true do
						local name, value = debug.getupvalue(a.__cb, i)
						if not name then break end
						if name == b then
							return value
						end
						i = i + 1
					end
				end

				return val
			end,
			__newindex = function(a,b,c)
				--print('n', a,b,c)
				if rawget(a.__env, b) then
					a.__env[b] = c
					return
				else
					-- look for locals
					local i = 1
					while true do
						local name, value = debug.getlocal(a.__level, i)
						--print('nv', name, value)
						if not name then break end
						if name == b then
							debug.setlocal(a.__level, i, c)
							i = -1
							return
						end
						i = i + 1
					end
					if i == -1 then return end

					-- look for upvalues
					if rawget(a, "__cb") then
						i = 1
						while true do
							local name, value = debug.getupvalue(a.__cb, i)
							--print('nv_cb', name, value)
							if not name then break end
							if name == b then
								debug.setupvalue(a.__cb, i, c)
								return
							end
							i = i + 1
						end
					end
				end
				rawset(a, b, c)
			end
		}) -- this line is the only level increase needed
		env.passVal = finder[VarName] -- finds the current value of the var
		print(finder[VarName])
		local typeStr = PGX.Vars.GetUpdateVarTypeString(typeof, VarName, ...)
		x = load([[
					local n, v = debug.getupvalue(debug.getinfo(2).func, 1)
					local isLocalValue = false;
					if n and n == ']]..VarName..[[' then
						isLocalValue = true
					end
					local ]]..VarName..[[_tmp = _ENV.passVal -- this upvalue is joined with the one from the caller function
					local y_ = function(p1)
						local _setValue = function(value)
							_ENV.passVal = value -- I HAVE NO IDEA WHY THIS IS NEEDED SOMETHING WITH MEMORY AS WE NEED TO CALL 'VALUE' BEFORE USING IT AS A VALUE
							]]..VarName..[[_tmp = value -- if the value is a local we set the joined upvalue
							if _ENV.]]..VarName..[[ then -- if the value is a global (or in a table) we set the value through the ENV
								_ENV.]]..VarName..[[ = value
							end
						end
						]]..typeStr..[[
					end
					if isLocalValue then -- to ensure we dont override the _ENV of the calling chunk
						debug.upvaluejoin(y_, 2, debug.getinfo(2).func, 1)
					end
					return y_()
				]], typeof..":"..VarName, "t", setmetatable({}, {
			__index = env,
			__newindex = env,
		}))
		return x
	end

This is really good for being able to be used for things like configs. Lets say you have a function to get a value from a centralized config, and set the value to a variable in a script (local or global). Then lets say that config was changed, most of the time you would have to create some sort of hook (Event handler or callback func) to catch the change and update the value, but this would mean each time, in each script, you would need to recreate this hook instead of some easy ~1 line function call to update the variables elsewhere. Saves dev time which does help.

Also since fivem events, promises, and func refs are all closed you are unable to lets say do debug.getinfo(2) inside of them to get the info of the function that called it (as we have cross resource communication). However with upvalues you can somewhat replicate it and modify values outside of a event handler using upvalues and the debug functions. Not to mention being able to update local values outside of scope (which I had thought impossible until I was finally able to do it with the code above).

I get that it is not a HUGE use case for the need of debug lib but it was helpful especially for devs.

Since I dont like complaining without suggesting solutions,

  1. make the client sandbox optional for servers to opt-out of like the upcoming changes to NUI-callbacks

  2. Add back at least setupvalue, upvaluejoin, upvalueid. Can also add some sort of tacking to it for modder abuse.

  3. Add workaround natives to be used to replicate some of the features loss, I would be willing to look into potentially trying to get this to work if it would actually be considered for a merge

Also after looking into my code more, getlocal should most definitely be open to be used too. Being able to check the names of functions parameters is very important. Get name of argument of function in lua - Stack Overflow

One use case for this would be overloading functions based on the number and name of each argument.

The debug library is inherently unsafe; setters for some fairly obvious reasons, and getters could still be used for malicious purposes.

even FiveM uses it in their standard scripts and scheduler

That doesn’t mean it needs to be available for standard users. A lot of that code is dated and could be handled outside the Lua environment, but isn’t really worth doing. We have access to many functions that we shouldn’t really be using. The debug library is for debugging, not writing functional code.

Thanks for your input and I respect the work you do and the stuff you have made but I do disagree with some of this.

We have access to many functions that we shouldn’t really be using.

I get in the name of modders and trying to stop them some action needs to be taken but this platform was made by and from ‘modders’ and developers. People being able to use the full extent of a coding language allows for innovation and allows for cool things that otherwise cant be made. At the end of the day these things sure could be somewhat unsafe but there has to be many other ways to go about this than just removing like 30% of LUA’s only way to somewhat keep up with JS. This literally shoots LUA in the leg as a language choice when it comes to script development (other than the fact that is already on its last leg).

That doesn’t mean it needs to be available for standard users. A lot of that code is dated and could be handled outside the Lua environment, but isn’t really worth doing.

Sure this is somewhat valid but my point stands that even removing these functions does not necessarily mean that modders can’t find another way around it and/or just find a way to reenable it (with enough tinkering and time this could be done as its all handled still on the client). As mentioned there are multiple solutions to this problem that can both help prevent abuse while not taking away developers tools. For instance, if we are trusting the client to not reverse engineer the Lua State or any of the dll’s or exe of the FiveM client, then we can safely add in modder/abuse checks into the debug lib functions as they are all defined in C.

If you check lua-users wiki: Sand Boxes many things are considered unsafe but removal would literally break ALL fivem LUA support. What’s next after this, removal of “load” or any of the raw functions. If we are treating the client as tho they can affect things outside the sandbox then we WILL have to disable a lot more than just the debug lib to truly get the results we are looking for. At the end of the day its a debug library, sure in prod we shouldn’t be using it but some things just cant be made without it, but we should as server owners have the option to select when it is appropriate to have it enabled and when we want it disabled.

Let me know if I got something wrong or missing something. I just want what is best for fivem as a whole, and I truly think this approach will not be a benefit in the way it is being done atm.

Unfortunately Lua lost by simply not having native support for require, which is also because of security reasons (being able to load C modules could be a disaster). Not really sure how having a bunch of unsafe functions from debug and io helps Lua keep up with JS, but your use cases are not something that shouldn’t be allowed. Getting and setting out of scope variables is insane and there is no real reason to have that power.

or just find a way to reenable it (with enough tinkering and time this could be done as its all handled still on the client)

Until you get banned…

many things are considered unsafe but removal would literally break ALL fivem LUA support

They wouldn’t break ALL Lua support at all. io was recently made sandbox-safe and will break very few scripts, and the team is willing to provide patches for other legitimate use-cases. Many of the functions listed in there as unsafe are already handled or not a concern.

What’s next after this, removal of “load”

They’ve already addressed that load will not be removed.

So it seems like you are massively over-complicating how to use upvalues. If you really want to update the value of a local variable using an upvalue, then just have a function which does that, i.e:

DynamicSetters = {}

local weaponsList = {}

DynamicSetters['weaponsList'] = function(value) weaponsList = value end

print(json.encode(weaponsList)) -- []

DynamicSetters['weaponsList']({ ['1'] = '2' })

print(json.encode(weaponsList)) -- {"1":"2"}