---
# System prepended metadata

title: 'C++ Pwning: Smart Pointers '

---

# Overview
> Smart pointers is wrapper class of normal pointer, which automatically release itself (by calling destructor) once **it goes out of scope**. Scope here might be inside a function. 

Hold on. Excuse me, what? We should not take it like this. Normal classes' destructors are also called once getting out of scope. Smart pointers like `shared_ptr, unique_ptr, etc.` are just part of them.


**The essence is that smart pointers are intended to be used as local variables** (in most cases, like class members or function variables). And similar to any other class variable allocated this way - which lifetime is **tied** to some sort of scope, the destructor will be called when it goes out of scope. This follows the principle of RAII.

## RAII
*They say RAII is probably the best feature of C++.*
> Resource Acquisition Is Initialization (RAII) is a C++ programming technique which **binds the life cycle of a resource** to **the lifetime of an object**. The **resource** here might be heap memory, thread of execution, open socket, open file, locked mutex, disk space, data base connection - which must be acquired before use.

To put it simply, the **lifetime** of an object refers to the period of time between object's construction and destruction. **Lifecycle** of a resource refer to the period of time between resource's **allocation** and **de-allocation** (free it, sort of). This is how it goes:
>  Allocate -> Object Constructor -> ... -> Object Destructor -> De-allocate

**Why: Safer memory usage, better performance without any garbage collection overhead**😂 Without this, class destructor must implement memory release operation itself, and that tends to leave bugs due to bad coding practices.

**Standard lib:** `std::vector`, `std::string`, smart pointers and `std::lock_guard`, `std::unique_lock`, etc. to manage mutexes.
Read more: [https://en.cppreference.com/w/cpp/language/raii](https://en.cppreference.com/w/cpp/language/raii)

<!-- ### Stack unwinding (C++ only) -->

## What is scope anyway
How do compiler define "scope"? How does it know when a smart pointer goes out of scope and insert some code to call the smart pointer destructor at that place?

> The same way everything in C++ knows it's gone out of scope - it reaches the end of the surrounding pair of {}s (or, if it is a member variable of another struct, the destructor of the parent will call the destructors of all child elements).
```cpp!=
#include <memory>

struct foo {
    std::unique_ptr<int> p;
};

int main() {
    std::unique_ptr<int> a;
    {
        std::unique_ptr<int> b;
    } // b now goes out of scope and is destructed
    {
        foo f;
    } // f now goes out of scope, meaning both f and f::p are destructed
} // a now goes out of scope and is destructed
```

### What if we attempt to call the destructor?
Compiler automatically inserts code to call to object destructor. Now we would like to make a call to destructor right before the end of function. It would result in calling destructor twice. 

**If the destructor is responsible for releasing some sort of memory, memory corruption would happen.**

# Smart Pointers
Now get back to smart pointers. Once the destructor is called, the wrapped object's destructor is also called.

- `~unique_ptr()` immediately calls the object's destructor.
- `~shared_ptr()` only decreases the `use_count` (reference count) by one until it reaches `-1` and then call the wrapped object's destructor later on.


## `std::shared_ptr<T>` internals

> Several shared_ptr instances can share the management of an object's lifetime **through a common control block**. The managed object is deleted when the last owning shared_ptr is destroyed (or is made to point to another object). Memory management by `shared_ptr` is **deterministic** because the timing of a managed object's destruction is predictable and **in the developer's control**.

Well, but how is it implemented? A `shared_ptr` contains 2 pointers (hence size is 16 in `x86-64`).

![control_block](https://hackmd.io/_uploads/rkEEeY5wA.png)


In this section, we'd like to focus on the second one - the pointer to **control block**.
The first pointer is the raw pointer of the object being wrapped - returned by `get()` method. More more information, read [Pwning in C++](https://hackmd.io/@trhoanglan04/pwningCpp) (Tiếng Việt).

An array of `shared_ptr<IOSymbol>` looks like this in memory:
![shared_ptr_array](https://hackmd.io/_uploads/rkVofd5PC.png)


Notice that:
- The `vtable` address are the same for all `share_ptr<IOSymbol>` instances (0x555555568860 and 0x5555555686d0). However, the control block addresses are different **heap address** due to the default `std::allocator`. The allocator can be specified. 
```
pwndbg> p (std::shared_ptr<IOSymbol>) *0x7ffff7a28058
$4 = {
  <std::__shared_ptr<IOSymbol, (__gnu_cxx::_Lock_policy)2>> = {
    <std::__shared_ptr_access<IOSymbol, (__gnu_cxx::_Lock_policy)2, false, false>> = {<No data fields>},
    members of std::__shared_ptr<IOSymbol, (__gnu_cxx::_Lock_policy)2>:
    _M_ptr = 0x7ffff7fa7050,
    _M_refcount = {
      _M_pi = 0x5555555e7090
    }
  }, <No data fields>}
```
- This is how the control block looks like:
```
pwndbg> telescope 0x5555555e7250 3
00:0000│  0x5555555e7250 —▸ 0x5555555686d0 (vtable for std::_Sp_counted_ptr<IOSymbol*, (__gnu_cxx::_Lock_policy)2>+16) —▸ 0x55555555b162 (std::_Sp_counted_ptr<IOSymbol*, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr()) ◂— endbr64
01:0008│  0x5555555e7258 ◂— 0x100000002
02:0010│  0x5555555e7260 —▸ 0x7ffff7fa73d0 —▸ 0x555555568828 (vtable for IOSymbol+16) —▸ 0x5555555576fa (IOBytes::compare(ArrayTemplate<unsigned char> const&)) ◂— endbr64
```

What is that `0x100000002`? Weak count and ref count right?






### Operators
- Assign: [std::shared_ptr<T>::operator=](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator%3D)
    If the current pointer points to an existing object before the assignment, that existing object would be destroyed and the new object being assigned's `share_cnt` (reference count) is increased.
- Swap: [std::shared_ptr<T>::swap](https://en.cppreference.com/w/cpp/memory/shared_ptr/swap) 
    
## `std::weak_ptr`

    
# Pwn it 🍒
# Rust



# Summary

# References
- [Reddit: how_does_a_smart_pointer_know_when_its_gone_out/](https://www.reddit.com/r/cpp_questions/comments/ecmzat/how_does_a_smart_pointer_know_when_its_gone_out/)
- [shared_ptr - basics and internals with examples](https://www.nextptr.com/tutorial/ta1358374985/shared_ptr-basics-and-internals-with-examples)
