mprotect排查全局变量内存被篡改问题

1. 问题背景
最近公司一直使用的上位机工具更新,使用已发售的设备对上位机进行测试,发现在关闭设备水印、开启画面翻转后,导出设备配置,再将设备恢复默认配置,重新再导入配置后设备死机。应用组同事根据生成的Core文件定位到死机点位于视频组件库,于是拿着Core文件找到我们一起排查。
2.问题分析
看一下Core文件:
#0 0x0005970c in GetMaxFps (pFps=0x6e8ebcb0) at …/src/iot/vin.c:254
看一下代码:
status = GetVinCfg(0, &pVideoInCfg);
if (xxxxx)
{
pFormat = pVideoInCfg->pStandP;
}
else if (xxxxxx)
{
pFormat = pVideoInCfg->pStandN;
}
看下地址:
(gdb) p pVideoInCfg
$1 = (VSF_VinConfig *) 0x0 // 空指针,导致段错误
再看一下status = GetVinCfg(0, &pVideoInCfg)调用:
static Int32 GetSmartWizardVinCfg(Uint32 channel, VinConfig **ppVinCfg)
{
*ppVinCfg = gCfgObj.pVinCfg; // 全局变量,设备刚启动时会对全局变量内存进行初始化
return SW_TRUE;
}
再在gdb中看一下gCfgObj变量的内存情况:
在这里插入图片描述
typedef struct
{
xxxxxxx pdtCfg;
xxxxxxx streamCfg[0][3];
xxxxxxxx *pVinCfg;
xxxxxxxxx *pChnMapInfo;
}SmartWizard_Config;
全局变量gCfgObj中的streamCfg成员是二维数组,根据对全局变量gCfgObj初始化部分的代码分析,encMask的值不应该为9961632,基本可以认定该问题是全局gCfgObj内存被篡改,并且通过打印与gCfgObj临近的高地址内存也同样存在内存异常的现象。
3. 解决手段
确定了该问题是由于全局变量被篡改导致的,我们就需要找到篡改该内存的代码所在的线程,通常有两种方法:
1、使用GdbServer在线调试方法,对被篡改的内存添加条件断点。如在本案例中,根据代码流程,encMask的正常值应该为7,添加条件断点watch encMask > 7. // 厂商未提供该平台的GDbserver版本,放弃
2、在被篡改的内存区域中间插入内存保护区域,当异常代码尝试修改内存保护区域内容时,触发段错误。通过打印gCfgObj.pChnMapInfo地址为0x6c2d94,下一页的起始地址为0x6c3000(页大小为4Kb), 因此,在结构体SmartWizard_Config中增加一段内存保护区域,在初始化gCfgObj全局变量代码结束后,使用mprotect((void *)mpAddrAlign, ALIGN_SIZE,PROT_READ)将内存保护区域设置为只读。因为与gCfgObj临近的高地址内存也同样存在内存被篡改的问题,增加保护区域后,以前对高地址的篡改就转移到了对保护区域的修改。
typedef struct
{
xxxxxxx pdtCfg;
xxxxxxx streamCfg[0][3];
xxxxxxxx *pVinCfg;
xxxxxxxxx *pChnMapInfo;
Uint8 mpAreal[4824]; // 内存保护区域
}SmartWizard_Config;
4.结果验证
使用方法2打包程序升级设备后,按照问题背景中描述的复现方法操作,产生Core文件, 分析新生成的Core文件:
(gdb) bt
#0 0x74ed22d4 in __memcpy_neon () from /home/levol/opt/arm-linux-gnueabihf-6.5/arm-linux-gnueabihf/sysroot/lib/libc.so.6
#1 0x000446cc in UpdateOsdMem (pTitle=pTitle@entry=0x6f7d95f8) at …/src/osd.c:497
#2 0x0004ae48 in videoEncSetTitle (title=0x6f7d95f8, title@entry=0x6f7d95f0) at …/src/enc.c:337
看一下代码:
Int32 UpdateOsdMem(Uint32 chId, Int32 streamId, AppTitle *pTitle)
{
OsdObj *pObj = &gOsdObj;
OsdChn *pChnObj = NULL;
Uint32 idx = pTitle->index;
pChnObj = &pObj->chnObj[chId];
Title *pVideoTitle = &pChnObj->osdTitle[streamId][idx];
OSA_memCpy(&pVideoTitle->title,pTitle); // 死机点
}
查看下pVideoTitle->title地址为0x6c31c0, 而我们添加的内存保护区域为0x6c3000 - 0x6c406f,刚好处于我们的设置的内存保护区域。进一步打印出pTitle->index为83,而正常下发的index范围应该是0-4, 最终确定为应用下发的参数错误,导致&pChnObj->osdTitle[streamId][idx]地址越界,问题交回应用排查。

  • 26
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值