野指针操作导致的崩溃: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初阶--指针初阶(上):什么是指针+指针类型+野指针(下)
|
6月前
|
存储 Perl
C初阶--指针初阶(上):什么是指针+指针类型+野指针(上)
C初阶--指针初阶(上):什么是指针+指针类型+野指针(上)
|
1月前
|
安全 程序员 编译器
了解野指针与assert断言 拿捏指针的使用!
了解野指针与assert断言 拿捏指针的使用!
|
1月前
|
安全 程序员 编译器
[C语言]指针进阶之野指针与assert断言
[C语言]指针进阶之野指针与assert断言
|
6月前
|
存储 Linux 编译器
Linux系统中指针的详细分析与操作
Linux系统中指针的详细分析与操作
68 1
|
2月前
|
C++
C++野指针 空指针 危险指针
C++野指针 空指针 危险指针
|
5月前
|
存储 算法 编译器
指针(一)------指针概念+指针类型+野指针+指针运算+二级指针
指针(一)------指针概念+指针类型+野指针+指针运算+二级指针
46 0
|
6月前
|
存储 C语言
C语言之指针的含义,指针类型的定义及使用方法,野指针的定义,以及原因,如何避免野指针
C语言之指针的含义,指针类型的定义及使用方法,野指针的定义,以及原因,如何避免野指针
|
9月前
学C的第十七天【指针初阶: 1. 指针是什么?;2. 指针和指针类型;3. 野指针;4. 指针运算】2
3. 野指针 概念:野指针就是这种指向的位置时不可知的(随机的、不正确的、没有明确限制的)
|
9月前
|
存储
学C的第十七天【指针初阶: 1. 指针是什么?;2. 指针和指针类型;3. 野指针;4. 指针运算】1
1. 指针是什么? (1). 指针是内存中一个最小单元的编号,也就是地址 (2). 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量 总结:指针就是地址,口语中说的指针通常指的是指针变量