what is c++ shared_ptr

c++ 문제를 풀다보니

C
    Smart *smart = new Smart();
    std::shared_ptr<Smart> src_ptr(smart);
    std::shared_ptr<Smart> new_ptr(smart);

다음과 같은 코드가 나왔다. 처음 보는 생소한 코드여서 알아보도록 하자

shared_ptr 객체는 한가지 포인터에 대해 다같이 사용할 수 있게 해주는 객체이다. 몇개가 해당 포인터에 이어져 있는지도 확인할 수 있으며, 마지막으로 이어진 곳에서 포인터를 놓을때 해당 shared_ptr의 메인 객체도 free가 되어 메모리를 관리하기 쉽게 되어있다.

C
pwndbg> stack
00:0000│ rsp     0x7fffffffe070 ◂— 0
01:0008│-058     0x7fffffffe078 —▸ 0x6172b0 —▸ 0x4015b4 (apple()) ◂— pushq %rbp
02:0010│-050     0x7fffffffe080 —▸ 0x6172b0 —▸ 0x4015b4 (apple()) ◂— pushq %rbp
03:0018│-048     0x7fffffffe088 —▸ 0x6172d0 —▸ 0x402300 (vtable for std::_Sp_counted_ptr<Smart*, (__gnu_cxx::_Lock_policy)2>+16) —▸ 0x401fe0 (std::_Sp_counted_ptr<Smart*, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr()) ◂— pushq %rbp
04:0020│-040     0x7fffffffe090 —▸ 0x6172b0 —▸ 0x4015b4 (apple()) ◂— pushq %rbp
05:0028│ rcx rdi 0x7fffffffe098 —▸ 0x6172f0 —▸ 0x402300 (vtable for std::_Sp_counted_ptr<Smart*, (__gnu_cxx::_Lock_policy)2>+16) —▸ 0x401fe0 (std::_Sp_counted_ptr<Smart*, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr()) ◂— pushq %rbp
06:0030│-030     0x7fffffffe0a0 ◂— 0
07:0038│-028     0x7fffffffe0a8 ◂— 0
pwndbg> x/3gx 0x6172d0
0x6172d0:	0x0000000000402300	0x0000000100000001
0x6172e0:	0x00000000006172b0	
pwndbg> 

생성 했을때 스택에 저장되는 것을 살펴보면

C
02:0010│-050     0x7fffffffe080 —▸ 0x6172b0 —▸ 0x4015b4 (apple()) ◂— pushq %rbp
03:0018│-048     0x7fffffffe088 —▸ 0x6172d0 —▸ 0x402300 (vtable for std::_Sp_counted_ptr<Smart*, (__gnu_cxx::_Lock_policy)2>+16) —▸ 0x401fe0 (std::_Sp_counted_ptr<Smart*, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr()) ◂— pushq %rbp

16바이트를 차지하고

첫번째 8바이트에는 sharded 하고 있는 메인 객체의 주소를 나타낸다 ( 0x6172b0 )

두번째 8바이트에는 control block 객체의 주소를 나타낸다. ( 0x6172d0 )

해당control block을 봐보면

C
pwndbg> x/5gx 0x6172d0
0x6172d0:	0x0000000000402300	0x0000000100000001
0x6172e0:	0x00000000006172b0	

0x0000000000402300 → vtable 주소 ( shared_ptr가 가지고 있는  destroy(), dispose() 같은 내부 가상 함수 주소들 )

0x0000000100000001 → 각각 4바이트로 각각 weak_count , use_count (shared count) 0x00000000006172b0 → 메인 객체의 주소 와 같은 정보들을 담고 있다.

vulnerability

참고로 위에서 보았던,

C
    Smart *smart = new Smart();
    std::shared_ptr<Smart> src_ptr(smart);
    std::shared_ptr<Smart> new_ptr(smart);

이런 사용법은 완전히 위험하고 의도와 맞지 않은 코드이다.

이러면 smart에 대해서 각각 공유하고 있다고 생각하기에 2개가 서로 공유를 하던 의도랑 완전히 달라질 수 있다. ( uaf가 가능하게 됨 )

따라서, 아래와 같이 사용해야 한다.

C
Smart* smart = new Smart();
std::shared_ptr<Smart> src_ptr(smart);
std::shared_ptr<Smart> new_ptr = src_ptr; 
C
std::shared_ptr<Smart> smart = std::make_shared<Smart>();
std::shared_ptr<Smart> src_ptr(smart);
std::shared_ptr<Smart> new_ptr(smart);