How to modify a metatable which was created by C API? - lua

I want to add some methods or properties to a lua object witch metadata was created by C API. I can't add property in normal way, for example:
local foo = libc.new()
foo.bar = "hello"
it say:
Failed to run script: attempt to index a libc_meta value (local 'foo')
So I think maybe need to modify metatable, so I change my code:
local foo = libc.new()
local mt = getmetatable(foo)
foo[bar] = "hello"
setmetable(foo, mt)
Unfortunately, it still doesn't work.
Failed to run script: bad argument #1 to 'setmetatable' (table expected, got libc_meta)
So how can I add methods or properties to this 'foo'?
BTW, c code is here:
static int libc_new(lua_State *L) {
...
lua_lib_space *libc = lua_newuserdata(L, sizeof(*libc));
libc->L = L;
libc->cb_enter = cb_enter;
libc->cb_leave = cb_leave;
luaL_getmetatable(L, "libc_meta");
lua_setmetatable(L, -2);
lib_space *lib = lib_new(enter, leave, libc);
libc->space = lib;
return 1;
}

Userdata is meant to be created by C, manipulated by C code, and for only the purposes that C code intends. Lua can talk to it, but only in the ways that C allows it to. As such, while userdata can have a metatable (and those metamethods are the only way Lua can "directly" interact with the userdata), only C functions (and the debug library, but that's cheating) can directly manipulate that metatable. Lua is an embedded language, and C has primacy; if some C library wants to shut you out, it can.
What you can do is take the userdata and stick it in a table of your own, then give that table a metatable. In your __index metamethod, if you have an override or new method for a particular key, then you forward the accesses to that. Otherwise, it will just access the stored userdata.
However, if what you get in the userdata is a function, then you may have a problem. See, most userdata functions take the userdata as a parameter; indeed, most are meant to be called via "ud:func_name(params). But if thatud` is actually the table wrapper, then the first parameter passed to the function will be the wrapper, not the userdata itself.
That poses a problem. When the thing you get from the userdata is a function, you would need to return a wrapper for that function which goes through the parameters and converts any references to the wrapper table into the actual userdata.

Related

Is there a way to block an iteration of a table in Lua 4.0 ? (or how to prevent the access of table content)

I want to lock the access of a table content in Lua 4.01.
Unfortunately I can't upgrade to Lua 5.xx.
I was thinking to use tag method (old metatable/metamethod mechanism of Lua) but it is still possible to traverse the table without triggering tagmethods using for loop (for ie,e in table do ...).
It's like for statement uses rawget to access the table content.
Is there a way to lock the access?
I know it would be possible using the C API but it's not really possible for the end-user.
Thanks
Using a table as un upvalue is a way to control data visibility. See Visibility and Upvalues in the Lua 4.0 Reference Manual.
You maintain your data in a table local to a function. That table can’t be seen outside of that function.
An example:
function a()
local t = {data = 123}
return function()
print(t.data)
end
end
Then:
b = a()
b() -- prints “123”
Bad practice here to just use letters for function names, but it gets the point across: b is just the table returned from calling a. But this b, when called, prints the data stored in a’s local table t. There is no other way to access t, so in this way you can control table access. Add more functionality (setters, getters, other logic) to a’s return table to control access.
Another Example
Showing getter and setter access:
function a()
local t = {data = nil}
local function set(data)
t.data = data
end
local function get()
return t.data
end
return {
set = set,
get = get
}
end
b = a()
b.set("abc")
print(b.get())
b.set(123)
print(b.get())
This prints:
abc
123

Lua setfenv/metatable sandbox won't work with ROBLOX methods

In ROBLOX Lua, I'm writing a game that involves users creating and running Lua scripts. Obviously, I need to prevent the use of certain services and functions, such as the Kick function on the Player class, or anything related to DataStore or TeleportService.
So far, I've been successful at making a sandboxed environment by using setfenv to set a function's environment to a metatable, which is attached to a "sandbox" table. On __index, if nothing is found in the sandbox table, it looks in the real environment like it normally would. This allows me to put fake functions in the sandbox table that will be used instead of their real counterparts.
However, let's say I sandboxed the ClearAllChildren function. Players could easily escape the sandbox by doing this:
someObject.Parent.someObject:ClearAllChildren()
This is because getting an instance's Parent gives them the real version as opposed to the sandboxed version. This flaw can be pulled off many other ways, too.
So I made an object wrapper. Calling wrap(obj) on an instance returns a fake version created with newproxy(true). The __index of its metatable makes sure any child of the object (or instance property such as Parent) will return a wrapped version.
My issue is likely with the way I have my wrapper set up. Attempting to call any method on an object inside the sandbox, like this:
x = someObject:GetChildren()
Results in the following error:
Expected ':' not '.' calling member function GetChildren
Here's the full code for my sandbox currently:
local _ENV = getfenv(); -- main environment
-- custom object wrapper
function wrap(obj)
if pcall(function() return obj.IsA end) then -- hacky way to make sure it's real
local realObj = obj;
local fakeObj = newproxy(true);
local meta = getmetatable(fakeObj);
meta['__index'] = function(_, key)
-- TODO: logic here to sandbox wrapped objects
return wrap(realObj[key]) -- this is likely the source of method problem
end;
meta['__tostring'] = function()
return realObj.Name or realObj;
end;
meta['__metatable'] = "Locked";
return fakeObj;
else
return obj;
end;
end;
-- sandbox table (fake objects/functions)
local sandbox = {
game = wrap(game);
Game = wrap(Game);
workspace = wrap(workspace);
Workspace = wrap(Workspace);
script = wrap(script);
Instance = {
new = function(a, b)
return wrap(Instance.new(a, b))
end;
};
};
-- sandboxed function
function run()
print(script.Parent:GetChildren())
print(script.Parent)
script.Parent:ClearAllChildren()
end;
-- setting up the function environment
setfenv(run, setmetatable(sandbox, {__index = _ENV;}));
run();
How can I fix this?

NSE Script - Confusion with the use of self on table's function

Good day, I'm trying to understand a little more NSE scripts that are based on Lua, but there is something that I can't understand related to syntax when using functions as elements from a table. I'm going to show parts of the script nmap/scripts/broadcast-rip-discver.nse where I'm getting lost:
RIPv2 = {
-- The Request class contains functions to build a RIPv2 Request
Request = {
-- Creates a new Request instance
--
-- #param command number containing the RIPv2 Command to use
-- #return o instance of request
-- code ommitted (give values to the table o)
setmetatable(o, self)
self.__index = self
return o
end,
-- Converts the whole request to a string
__tostring = function(self)
--- -- code ommitted ( Override the metafunction __tostring)
return data
end,
},
-- The Response class contains code needed to parse a RIPv2 response
Response = {
-- Creates a new Response instance based on raw socket data
--
-- #param data string containing the raw socket response
-- #return o Response instance
new = function(self, data)
local o = { data = data }
-- code ommitted (Read from data and pass values to o)
setmetatable(o, self)
self.__index = self
return o
end,
}
}
And from the "action" part of the script we have a use like this
local rip = RIPv2.Request:new(RIPv2.Command.Request)
local response = RIPv2.Response:new(data) -- Data has been already give a value
I understand that is "similar" to create a new instance of the Table RIPv2 for those two lines. As all the function are inside of a table (that is not a class because Lua only have basic tools for make things similar but no the same to class) so the "self" argument it's mandatory for Lua have a idea to where place that.
But what I can't understand it's why try to override functions from the table RIPv2 to the table o, I mean the lines what objective has?:
setmetatable(o, self) )
I understand that the variable table o could have now the same functions that RIPv2 together his own values but this part make me crazy and I can't find a straight answer at Nmap's Forum.
P.d. which would be the difference to declare RiPv2 with "local" (Be sure that is not a Global variable)
As all the function are inside of a table so the "self" argument it's mandatory for Lua have a idea to where place that.
All the functions aren't necessarily part of the table. A metatable allows you to specify that lookups in table a can resolve to lookups in table b, and that's exactly what's being done here.
But what I can't understand it's why try to override functions from the table RIPv2 to the table o, I mean the lines what objective has?: setmetatable(o, self) )
o is an instance of a class, it contains only the instance data. The methods are stored in class objects Request or Response. The metatable allows attempts to index o to resolve via the class object.
See the Programming in Lua chapter on classes.

Calling Lua from C

I'm trying to call a user-defined Lua function from C. I've seen some discussion on this, and the solution seems clear. I need to grab the index of the function with luaL_ref(), and save the returned index for use later.
In my case, I've saved the value with luaL_ref, and I'm at a point where my C code needs to invoke the Lua function saved with luaL_ref. For that, I'm using lua_rawgeti as follows:
lua_rawgeti(l, LUA_REGISTRYINDEX, fIndex);
This causes a crash in lua_rawgeti.
The fIndex I'm using is the value I received from luaL_ref, so I'm not sure what's going on here.
EDIT:
I'm running a Lua script as follows:
function errorFunc()
print("Error")
end
function savedFunc()
print("Saved")
end
mylib.save(savedFunc, errorFunc)
I've defined my own Lua library 'mylib', with a C function:
static int save(lua_State *L)
{
int cIdx = myCIndex = luaL_ref(L, LUA_REGISTRYINDEX);
int eIdx = luaL_ref(L, LUA_REGISTRYINDEX);
I save cIdx and eIdx away until a later point in time when I receive some external event at which point I would like to invoke one of the functions set as parameters in my Lua script. Here, (on the same thread, using the same lua_State*), I call:
lua_rawgeti(L, LUA_REGISTRYINDEX, myCIndex);
Which is causing the crash.
My first suggestion is to get it working without storing the function in C at all. Just assign your function to a global in Lua, then in C use the Lua state (L) to get the global, push the args, call the function, and use the results. Once that's working, you've got the basics and know your function is working, you can change the way you get at the function to use the registry. Good luck!
As #Schollii mentioned, I was making this call after doing a lua_close(L).

Forward define a function in Lua?

How do I call a function that needs to be called from above its creation? I read something about forward declarations, but Google isn't being helpful in this case. What is the correct syntax for this?
Lua is a dynamic language and functions are just a kind of value that can be called with the () operator. So you don't really need to forward declare the function so much as make sure that the variable in scope when you call it is the variable you think it is.
This is not an issue at all for global variables containing functions, since the global environment is the default place to look to resolve a variable name. For local functions, however, you need to make sure the local variable is already in scope at the lexical point where you need to call the value it stores, and also make sure that at run time it is really holding a value that can be called.
For example, here is a pair of mutually recursive local functions:
local a,b
a = function() return b() end
b = function() return a() end
Of course, that is also an example of using tail calls to allow infinite recursion that does nothing, but the point here is the declarations. By declaring the variables with local before either has a function stored in it, those names are known to be local variables in lexical scope of the rest of the example. Then the two functions are stored, each referring to the other variable.
You can forward declare a function by declaring its name before declaring the actual function body:
local func1
local func2 = function()
func1()
end
func1 = function()
--do something
end
However forward declarations are only necessary when declaring functions with local scope. That is generally what you want to do, but Lua also supports a syntax more like C, in which case forward declaration is not necessary:
function func2()
func1()
end
function func1()
--do something
end
Testing under the embedded lua in Freeswitch, forward declaration does not work:
fmsg("CRIT", "It worked.")
function fmsg(infotype, msg)
freeswitch.consoleLog(infotype, msg .. "\n")
end
result:
[ERR] mod_lua.cpp:203 /usr/local/freeswitch/scripts/foo.lua:1: attempt to call global 'fmsg' (a nil value)
Reversing the order does (duh) work.
To comprehend how forward referencing in Lua works compared to C, you must understand the a fundamental difference between C compilation and the Lua execution.
In C, forward referencing is a compile time mechanism. Hence if you include a forward declaration template in a C module then any of your code following will employ this template in compiling the call. You may or may not include the function implementation in the same module, in which case both declarations must be semantically identical or the compiler will error. Since this is a compile time construct, the compiled code can be executed in any order.
In Lua, forward referencing is runtime mechanism, in that the compiled function generates a function prototype internally within the code, but this is only accessible as a runtime Lua variable or value after the execution has
passed over the declaration creating a Lua closure. Here the declaration order within the source is immaterial. It is the execution order that is important: if the closure hasn't been bound to the variable yet, then the execution will throw a "nil value" exception.If you are using a local variable to hold the function value, then normal local scoping rules still apply: the local declaration must precede its use in the source and must be within scope, otherwise the compiler will compile in the wrong global or outer local reference. So forward referencing using locals as discussed in other answer will work, but only if the Protos are bound to closures before the first call is executed.
Doesn't work for me if I try to call the function before definition. I am using this Lua script in nginx conf.
lua entry thread aborted: runtime error: lua_redirect.lua:109: attempt to call global 'throwErrorIfAny' (a nil value)
Code snippet -
...
throwErrorIfAny()
...
function throwErrorIfAny()
ngx.say("request not allowed")
ngx.exit(ngx.HTTP_OK)
end
Given some other answers have also pointed out that it didn't work for them either, it is possible that forward declaration of Lua doesn't work with other tools.
PS : It works fine if I put the function definition before and then call it after wards.
If you use OOP you can call any function member prior its "definition".
local myClass = {}
local myClass_mt = { __index = myClass }
local function f1 (self)
print("f1")
self:later() --not yet "delared" local function
end
local function f2 (self)
print("f2")
self:later() --not yet "declared" local function
end
--...
--later in your source declare the "later" function:
local function later (self)
print("later")
end
function myClass.new() -- constructor
local this = {}
this = {
f1 = f1,
f2 = f2,
later = later, --you can access the "later" function through "self"
}
setmetatable(this, myClass_mt)
return this
end
local instance = myClass.new()
instance:f1()
instance:f2()
Program output:
f1
later
f2
later

Resources