IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    c++ protobuf中set_allocated引起的double free core dump

    五四陈科学院发表于 2016-03-24 16:09:04
    love 0

    以下内容由[五四陈科学院]提供

    c

    在c++中使用protobuf的时候,大多数元素,我们可以直接set_xxx,如果有嵌套进去一个对象,会有set_allocated_xxx的方法和mutable_xxx的方法。

    这里有一个坑。

    现象

    首先,看pb定义:

    package test;
    message a{
        required uint32 aa = 1;
    }
    
    message b{
        required a aaa = 1;
    }

    如果使用set_allocated_aaa,同时传入了一个定义好的a,而不是new的a,如下:

    void bad_case(){
        a aa;
        b bb;
        aa.set_aa(1);
        bb.set_allocated_aaa(&aa);
    }

    编译的时候不会有错误,一运行就出问题了:

    *** glibc detected *** ./test.run: double free or corruption (out): 0x00007fffc65ade20 ***

    正确的写法

    下面的写法,不会有问题:

    int good_case1(){
        a* aa = new a();
        b bb;
        aa->set_aa(1);
        bb.set_allocated_aaa(aa);
        return 0;
    }

    下面的写法,也不会有问题:

    void good_case2(){
        a aa;
        b bb;
        aa.set_aa(1);
        bb.mutable_aaa()->MergeFrom(aa);
    }

    原因

    在pb生成的对象中,析构函数统一都要进行对象的delete操作:

    b::~b() {
      // @@protoc_insertion_point(destructor:test.b)
      SharedDtor();
    }
    
    void b::SharedDtor() {
      if (this != default_instance_) {
        delete aaa_;
      }
    }

    通过定义得到的一个变量,只在栈上临时生成,在函数生命周期后自动清理,而将地址给了一个pb后,执行结束时会进行手动delete,从而导致了double free。

    通过new得到的一个对象,放在堆上,手动delete才会清理。

    good_case2正确的原因,是因为mutable_aaa的代码里new了一个a。

    inline ::test::a* b::mutable_aaa() {
      set_has_aaa();
      if (aaa_ == NULL) aaa_ = new ::test::a;
      // @@protoc_insertion_point(field_mutable:test.b.aaa)
      return aaa_;
    }

    结论

    使用pb的set_allocated_xxx要小心,没有显式allocate的东西不要往里传。

    文中涉及的代码地址:https://github.com/54chen/test


    想快点找到作者也可以到Twitter上留言: @54chen
    或者你懒得带梯子上墙,请到新浪微博:@54chen


沪ICP备19023445号-2号
友情链接