如何为 LUA 创建安全的 C 接口
我正在研究如何在我的应用程序中集成完整的脚本支持,但在规划我的 C API 以使其对 LUA 友好时遇到了一些问题。
基本上我得到了一堆通过 init 和 free 创建的结构 函数如下:
[ test.h ]
typedef struct
{
char name[ 50 ];
} Test;
Test *TestAdd( char *name );
Test *TestDelete( Test *test );
[ test.c ]
Test *TestAdd( char *name )
{
Test *test = ( Test * ) calloc( 1, sizeof( Test ) );
strcpy( test->name, name );
return test;
}
Test *TestDelete( Test *test )
{
free( test );
return NULL;
}
我使用 swig 生成 LUA 模块,因此我创建以下接口文件:
[ test.i ]
%module test
%{
%}
Test *TestAdd( char *name );
Test *TestDelete( Test * test );
如果用户代码如下所示,则一切正常:
a = test.TestAdd( "test" )
a = test.TestDelete( a )
if( a != nil ) print( a.name )
但如果用户代码是这样的:
a = test.TestAdd( "test" )
test.TestDelete( a )
if( a != nil ) print( a.name ) -- Crash the app with bad_access (not just a LuaVM error).
或者更糟糕的是:
a = test.TestAdd( "test" )
test.TestDelete( a )
test.TestDelete( a )
-- Another way of making crash my app completely!
有什么方法可以用 C 创建一组 API 来避免此类问题,并允许用户以安全的方式安全地添加/删除和访问属性创建“错误访问”错误并进行整个程序崩溃,最好的办法是 LUAVM 只是返回一个错误并继续执行。
我一直在搜索并尝试不同的 C API 方法来避免这个问题,但失败了......
任何人都可以帮助我或给我一些关于这个方向的指示。
提前致谢,
Im researching about how to integrate full script support inside my app but have a bit of a problem planning my C API to be LUA friendly.
Basically I got bunch of structs that are created through init and free
functions like this:
[ test.h ]
typedef struct
{
char name[ 50 ];
} Test;
Test *TestAdd( char *name );
Test *TestDelete( Test *test );
[ test.c ]
Test *TestAdd( char *name )
{
Test *test = ( Test * ) calloc( 1, sizeof( Test ) );
strcpy( test->name, name );
return test;
}
Test *TestDelete( Test *test )
{
free( test );
return NULL;
}
Im using swig to generate the LUA module so I create the following interface file:
[ test.i ]
%module test
%{
%}
Test *TestAdd( char *name );
Test *TestDelete( Test * test );
Everything work fine if the user code is like this:
a = test.TestAdd( "test" )
a = test.TestDelete( a )
if( a != nil ) print( a.name )
But if the user code is like that:
a = test.TestAdd( "test" )
test.TestDelete( a )
if( a != nil ) print( a.name ) -- Crash the app with bad_access (not just a LuaVM error).
Or even worst:
a = test.TestAdd( "test" )
test.TestDelete( a )
test.TestDelete( a )
-- Another way of making crash my app completely!
Is there any way that I can create a set of API in C that would avoid this kind of problem and would allow the user to safely add/delete and access properties in a safe way that would not create "bad access" error and make the whole program crash, the best would be that the LUAVM simply return an error and the execution continue.
I've been searching and tried different approach to my C API to avoid this problem but failed…
Anybody can help me or give me some pointers about the direction to go with this.
Thanks in advance,
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
有一种方法可以做到这一点:停止将 C 风格的接口直接导出到 Lua。 C 不是 Lua,你永远不应该想让 Lua 程序表现得像 C 程序。
除非没有办法避免,否则 Lua 代码不应该释放任何东西。如果 Lua 代码显式地创建了某些东西,那么 Lua 代码应该在其生命周期内进行治理。这意味着您使用垃圾收集来删除内存:当 Lua GC 完成使用它时,您可以使用元方法通过您使用的任何调用来释放指针。
一般来说,您可以通过以下两种方式之一为 Lua 提供一个指向对象的指针:要么 Lua 拥有该指针,要么该指针指向一个比 lua_State 本身寿命更长的对象(因此始终可供 Lua 脚本使用)。有时您可能会向 Lua 提供一些它可以引用的临时对象,但应尽可能避免这种情况。
您需要做的是使用
%newobject
指令< /a> 告诉 SWIGTestAdd
返回一个需要删除的指针。然后,您需要将Test
对象与TestDelete
关联起来作为删除器,使用 %newfree 类型映射。这将正确地将Test
的所有权从 C 转移到 Lua。此时,C 绝对不应该手动删除它。让 Lua 和 SWIG 完成他们的工作即可。因此,
TestDelete
不应该直接暴露给 Lua。当 GC 检测到没有人再引用Test
实例时,它将隐式调用。There is a way to do this: stop exporting C-style interfaces directly to Lua. C is not Lua, and you should never intend for Lua programs to act like C programs.
Unless there is no way to avoid it, Lua code should never have to free anything. If Lua code explicitly creates something, then Lua code should have governance over its lifetime. That means you use garbage collection to delete the memory: when the Lua GC is finished with it, you use a metamethod to free the pointer with whatever call you use.
In general, you give Lua a pointer to an object in one of two ways: either Lua owns that pointer, or that pointer is to an object that will outlive the
lua_State
itself (and therefore will always be available to the Lua script). There are occasions where you might hand Lua some temporary object that it can reference, but these should be avoided whenever possible.What you need to do is use the
%newobject
directive to tell SWIG thatTestAdd
returns a pointer that needs to be deleted. Then, you need to associate theTest
object withTestDelete
as the deleter, using %newfree typemap. This will properly transfer ownership of theTest
from C to Lua. At which point, C should never delete it manually. Just let Lua and SWIG do their job.As such,
TestDelete
should not be exposed to Lua directly. It will be called implicitly when the GC detects that nobody is referring to theTest
instance anymore.