如何为 LUA 创建安全的 C 接口

发布于 2024-12-21 20:30:00 字数 1360 浏览 1 评论 0原文

我正在研究如何在我的应用程序中集成完整的脚本支持,但在规划我的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

骄兵必败 2024-12-28 20:30:00

有一种方法可以做到这一点:停止将 C 风格的接口直接导出到 Lua。 C 不是 Lua,你永远不应该想让 Lua 程序表现得像 C 程序。

除非没有办法避免,否则 Lua 代码不应该释放任何东西。如果 Lua 代码显式地创建了某些东西,那么 Lua 代码应该在其生命周期内进行治理。这意味着您使用垃圾收集来删除内存:当 Lua GC 完成使用它时,您可以使用元方法通过您使用的任何调用来释放指针。

一般来说,您可以通过以下两种方式之一为 Lua 提供一个指向对象的指针:要么 Lua 拥有该指针,要么该指针指向一个比 lua_State 本身寿命更长的对象(因此始终可供 Lua 脚本使用)。有时您可能会向 Lua 提供一些它可以引用的临时对象,但应尽可能避免这种情况。

您需要做的是使用%newobject指令< /a> 告诉 SWIG TestAdd 返回一个需要删除的指针。然后,您需要将 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 that TestAdd returns a pointer that needs to be deleted. Then, you need to associate the Test object with TestDelete as the deleter, using %newfree typemap. This will properly transfer ownership of the Test 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 the Test instance anymore.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文