野指针操作导致的崩溃:lua层持有已经被释放的node指针,再次操作导致崩溃

简介: 野指针操作导致的崩溃:lua层持有已经被释放的node指针,再次操作导致崩溃

崩溃代码

lua
复制代码
-- 创建一个node,但是并不加入到scene中,在下一帧就会被回收
local wildPointer = cc.Label:create();
wildPointer:setString("123");
-- 在下一帧,此时的wildPointer已经是野指针了,再次调用就会触发崩溃
wildPointer:setString("");

这种lua代码非常容易出现,比如一个lua变量持有了一个node,但是这个node被remove了,此时lua层再次操作这个node,就会直接出问题。

崩溃日志

less
复制代码
com.game.test        A  Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 17314 (GLThread 4719), pid 17231 (com.game.test)
pid-18570            A  pid: 17231, tid: 17314, name: GLThread 4719  >>> com.game.test <<<
pid-18570            A        #00 pc 005b9674  /data/app/~~2dSXjw-Pwc5t1uuaelP0sA==/com.game.test-HVaqUS1t_su3ikG9VESEIQ==/lib/arm/libcocos2dlua.so (lua_cocos2dx_Label_setString(lua_State*)+180) (BuildId: 2198ce3de806dfb68c9d6346167edc344b301702)
pid-18570            A        #01 pc 009165cc  /data/app/~~2dSXjw-Pwc5t1uuaelP0sA==/com.game.test-HVaqUS1t_su3ikG9VESEIQ==/lib/arm/libcocos2dlua.so (BuildId: 2198ce3de806dfb68c9d6346167edc344b301702)
pid-18570            A        #02 pc 00909055  /data/app/~~2dSXjw-Pwc5t1uuaelP0sA==/com.game.test-HVaqUS1t_su3ikG9VESEIQ==/lib/arm/libcocos2dlua.so (lua_pcall+20) (BuildId: 2198ce3de806dfb68c9d6346167edc344b301702)

通过ida,005b9674 反汇编结果

image.png

对应的代码如下,可以看到就是转换之后的cobj出现了问题

image.png

cobj此时是null,因为COCOS2D_DEBUG的原因,这个问题在release模式下直接崩溃闪退,debug版本还能提示异常。

解决办法

造成这个问题的根本原因是这种引用持有野指针的情况非常极限,想要复现都非常困难。

可以通过bugly后台,看到哪个函数崩溃,就在哪个函数里面将if(!cobj){return 0;}的逻辑打开,避免崩溃

但是这样要不停地强更新,而且频繁更新app主包,会造成用户流失的,所以这种办法治标不治本,在c++层做各种保护根本起不到任何作用,问题根源还是lua脚本的逻辑,所以只能在lua层修复了这个问题。

要修复问题必须复现才行,但是这种情况又非常极限难以复现,所以只能尽可能的还原现场,根据收集到的信息进行排查,顺着这个思路,我们首先想到的是堆栈,是tolua_tousertype转为null导致的崩溃,那么我们可以增加一个机制,发现转换类型指针失败,,立即收集堆栈相关的数据并上报到后台。

这对于解决问题非常有帮助,当收到这些数据后,可以尝试复现下现场,这样才能从根源上解决问题,并且修复的肯定是lua代码,直接热更就行了,根本不需要更新app。

具体实现思路

在函数tolua_tousertype下个钩子

  • tolua++.h

c++

复制代码
TOLUA_API void set_tousertype_nullhandler(void(*func)); // 设置钩子函数
  • tolua++.c
c++
复制代码
void (*_tousertype_nullhandler)() = NULL; // 钩子函数
TOLUA_API void set_tousertype_nullhandler(void(* func))
{
    _tousertype_nullhandler = func;
}
TOLUA_API void* tolua_tousertype (lua_State* L, int narg, void* def)
{
    if (lua_gettop(L)<abs(narg))
        return def;
    else
    {
        void* u;
        if (!lua_isuserdata(L, narg)) {
            if (!push_table_instance(L, narg)) return NULL;
        };
        u = lua_touserdata(L,narg);
        void* ret = (u==NULL) ? NULL : *((void**)u); /* nil represents NULL */
        if (ret == NULL && _tousertype_nullhandler != NULL)
        {
            _tousertype_nullhandler(); // 如果发现类型转换失败了,触发钩子函数
        }
        return ret;
    }
}

然后在钩子函数里面进行lua堆栈数据的收集工作,这里面用到了lua_getstack,lua_getinfo获取每一层堆栈,通过lua_getlocal获取每层堆栈的变量情况,这里就不再贴代码了。

堆栈数据格式

示例

ini
复制代码
<setString> at =[C]:-1               # 第1层
<func> at .\crash-lua/index.lua:82   # 第2层
  a:10                               # 第2层的相关变量 
  b:{a=1,c="c",d={d1=1,d2="d2",},}
  c:<function>0F5F5C78
[Lua] at .\core/testLayer.lua:122    # 第3层
  ref:<userdata>
  type:0

不过考虑到网络传输带宽,需要将数据压缩下再发送到服务器,格式就不需要精简了,没有必要。

这里就要开发配套的后台日志系统,又是一个新坑。



目录
相关文章
|
6月前
|
编译器 C语言
嵌入式C语言变量、数组、指针初始化的多种操作
嵌入式C语言变量、数组、指针初始化的多种操作
49 0
|
6月前
|
存储 NoSQL 关系型数据库
使用lua脚本操作redis
使用lua脚本操作redis
99 0
C初阶--指针初阶(上):什么是指针+指针类型+野指针(下)
C初阶--指针初阶(上):什么是指针+指针类型+野指针(下)
|
存储 Perl
C初阶--指针初阶(上):什么是指针+指针类型+野指针(上)
C初阶--指针初阶(上):什么是指针+指针类型+野指针(上)
|
6月前
|
算法 Linux C语言
【Linux系统编程】深入理解Linux目录操作:文件夹位置指针操作函数(telldir,seekdir,rewinddir)
【Linux系统编程】深入理解Linux目录操作:文件夹位置指针操作函数(telldir,seekdir,rewinddir)
78 0
|
5月前
|
C++
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
|
6月前
|
C++
在C和C++中,指针的算术操作
在C和C++中,指针的算术操作
|
6月前
|
存储 安全 编译器
使用unsafe库操作结构体的属性偏移和指针转换
【5月更文挑战第19天】Go语言是类型安全的,但通过`unsafe`包,可以进行结构体属性偏移量计算和指针转换。`unsafe.Offsetof`获取结构体字段的偏移量,`unsafe.Pointer`允许不同类型指针间的转换。然而,这可能导致类型安全屏障被绕过,若使用不当,会引发安全问题或panic。应谨慎使用`unsafe`,因为它不遵循GO 1兼容性准则。
54 0
使用unsafe库操作结构体的属性偏移和指针转换
|
存储 Linux 编译器
Linux系统中指针的详细分析与操作
Linux系统中指针的详细分析与操作
134 1
|
6月前
|
安全 程序员 编译器
了解野指针与assert断言 拿捏指针的使用!
了解野指针与assert断言 拿捏指针的使用!