从C++ std::shared_ptr 原理来看看栈溢出的危害

C++ std::shared_ptr 实现原理

上周五排查了一个由于XXX模块操作疏忽导致栈越界引发的我的模块的智能指针Crash问题,因此稍微研究了一下,以作参考:

shared_ptr共享被管理对象,同一时刻可以有多个shared_ptr拥有对象的所有权,当最后一个shared_ptr对象销毁时,被管理对象自动销毁

shared_ptr 实现

简单来说,shared_ptr实现包含了两部分,

  • 一个指向堆上创建的对象的裸指针,raw_ptr
  • 一个指向内部隐藏的、共享的管理对象。share_count_object

第一部分没什么好说的,第二部分是需要关注的重点:

  • use_count,当前这个堆上对象被多少对象引用了,简单来说就是引用计数。
  • weak_count,这个管理对象被多少个智能指针共享了,简单来说就是管理对象的引用计数。

不同指针创建的对同一个堆上对象的智能管理,并不共享管理对象,因此存在double free的可能性

_shared_ptr直接包含的裸指针,即raw prt,是为了实现一般指针的->,*等操作,通过__shared_count object间接包含的指针是为了管理对象的生命周期,回收相关资源。

换句话说,__shared_count object内部的use_count主要用来标记被管理对象的生命周期,weak_count主要用来标记管理对象的生命周期。

注意区分管理和被管理。

问题原因

了解了原理,可以看出std::share_ptr本身的raw ptr指向了堆上通过new创建的对象。但是其自身这个raw ptr却会如果在不当操作上,被修改,比如栈越界操作,就会被破坏,导致产生对非法对象的访问:

int i = 5;
NSLog(@"i address is %p", &i);

XXX::XX df = XXX::XX::buildDataFrame();
NSLog(@"sp0 address is %p", &df);

int a[1] = {1};

NSLog(@"k address is %p", a);

for (int i = 0; i < 10; i++) {
    a[i] = i;
}

NSLog(@"haha");

上述这段代码就会引发问题,这里XXX::XX的具体内部设计使用了经典的RAII,下文再表。