您尚未登录。

楼主 #1 2020-05-21 22:30:29

Quotation
会员
注册时间: 2018-10-04
已发帖子: 289
积分: 271.5

F1C100s看门狗失效bug

刚调了好几个小时的灵异bug,现象是启动后有时候会死机。这时候已经启动了看门狗,但狗没有起作用,系统一直卡在那不reset。
最后调出来是个空指针错误。空指针毕竟是编程比较常见的错误,空指针能导致看门狗无效,F1C100s的这个bug也是有点严重。

一番精简之后,找到了最小复现代码:
只要用一个基类指针,指向一块内容为0的区域,调用基类的第二个虚函数,就很容易使看门狗失效并死机。

这个bug可能跟编译器的具体实现(C++类对象内存布局)有关,我用的GCC,其他编译器不一定是这样。

class Base
{
public:
    virtual void f() = 0;
    virtual void g() = 0;
};


// main():
    // 设置看门狗,5s的时长

    uint8_t empty[1024];
    memset(empty, 0, sizeof(empty));
    Base* p = (Base*)∅  // 野指针,指向了一块内容全为0的内存

    feedWatchdog();  // 喂狗
    mdelay(10);  // 等待超过1500ms则不出bug,等待0ms也不出bug,这之间都会遇到bug

    p->g();  // 调用基类的第二个虚函数(第一个没问题),从这里就死机了。看门狗无效。

离线

楼主 #3 2020-05-22 10:21:12

Quotation
会员
注册时间: 2018-10-04
已发帖子: 289
积分: 271.5

Re: F1C100s看门狗失效bug

继续研究。C++对象内存布局虽然没有固定标准,是编译器实现的,但基本是按照《深度探索C++对象模型》的样子。
一个带有虚函数的类的指针,指向的内存区域最开头应该是类的虚函数表指针。该指针指向一个数组,数组成员是函数指针。

在上面的例子中,当调用p->g();时,由于p指向的内存区域全是0,所以被当做是地址0x0处开始为虚函数表。
g()是类的第二个虚函数,所以把地址0x4当做函数指针。
打印出地址0x0附近的内存看:

0: 0xea00000d
1: 0xe59ff014
2: 0xe59ff014
3: 0xe59ff014
4: 0xe59ff014
5: 0xe59ff014
6: 0xe59ff014
7: 0xe59ff014
8: 0x80000260

看到0x4地址处的内容是0xe59ff014,接下来会把0xe59ff014当做函数地址去调用。就是经过这个非法函数调用后,看门狗失效并且死机!
复现此bug的代码可以再精简为:

    typedef void (*FuncPtr)();
    FuncPtr f = (FuncPtr)0xe59ff014;
    f();  // 触发看门狗bug

继续做实验发现,并不是只有这一个特殊的地址调用会触发bug,实际上有很大范围的地址都能引起看门狗无效。
从 0xdba00000 ~ 0xffff4040,这个区域内的地址,如果赋值给函数指针来调用,都会引发看门狗失效bug!!小于0xdba00000或大于0xffff4040的地址调用,则能正常死机并触发看门狗。(具体地址会变化,还跟CPU频率有关,不是完全精确的范围。)
这个范围是很大的,也就是说如果程序写错了,还是会有挺大的可能性遇到此bug。

比如考虑下面这个常见的野指针访问代码,触发看门狗bug的概率并不小。

struct A
{
    int data;
    void (*func)();
};

A* a = (A*)malloc(sizeof(A));
// ...
free(a);
// a被释放,指向的地址重新分配后写入了其他内容
// ...
a->func();  // 野指针使用,危险!

离线

楼主 #7 2020-05-24 21:42:53

Quotation
会员
注册时间: 2018-10-04
已发帖子: 289
积分: 271.5

Re: F1C100s看门狗失效bug

staunchheart 说:

这两天临时出去有事了,所以没能按时试。
经测试RTT用GCC编译,没有这样的问题。WDT正确的工作了。

    rt_kprintf("start wdg test----------------------------------\n");
    struct  A* a = (struct A*)malloc(sizeof(struct A));
    // ...
    free(a);
    // a被释放,指向的地址重新分配后写入了其他内容
    // ...
    a->func();  // 野指针使用,危险!
    rt_kprintf("end wdg test----------------------------------\n");

我那个只是示意代码。是要向那块被释放的地址里写入合适的数据,使a->func的值为0xdba00000 ~ 0xffff4040。

离线

楼主 #11 2020-05-25 22:51:49

Quotation
会员
注册时间: 2018-10-04
已发帖子: 289
积分: 271.5

Re: F1C100s看门狗失效bug

我也还不知如何解决。

离线

楼主 #15 2020-06-16 10:06:54

Quotation
会员
注册时间: 2018-10-04
已发帖子: 289
积分: 271.5

Re: F1C100s看门狗失效bug

myxiaonia 说:

0xdba00000 ~ 0xffff4040,将这个虚地址段用mmu映射到sdram区,这个总可以完成的吧!这样cpu不会司机了,因为实地址是真实可用地址啊

多谢,简单测试了一下,这个方法是可以解决问题的。现在我是把从0xd0000000到0xffffffff的地址映射到0x90000000。
只测试了一些地址,也不敢保证所有的地址都没问题。毕竟不知道该bug产生的原理。不过确实是暂时解决了。

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn