簡體   English   中英

雙指針如何與 shared_ptr 一致?

[英]How does a double pointer reconcile with a shared_ptr?

預先披露:我認為整個事情都是胡說八道並且偶然“起作用”,但我發現這段代碼並且它似乎對足夠低的工作價值“有效”(因為它在運行時不會崩潰,這不會意思很多),我不明白為什么。

手頭的問題是extern "C" API 作為 DLL/so 公開,然后通過 FFI 調用(在本例中為 Python),但extern "C"代碼使用shared_ptr 然而它移動了。

C++代碼:

#include <memory>
 
extern "C" {
  int make(std::shared_ptr<int> p) {
    p = std::make_shared<int>(42);
    return 0;
  }
 
  int get(std::shared_ptr<int> p) {
    return *p;
  }
}

呼叫者,召集者:

import ctypes
 
lib = ctypes.CDLL('lib.so')
 
p = ctypes.c_void_p()
lib.make(ctypes.byref(p))
print(lib.get(ctypes.byref(p)))

將 C++ 代碼構建為共享庫(名為lib.so )后,Python 代碼運行良好並打印42 此代碼已在使用 Clang 編譯的 macOS/ARM64 上進行了測試,但據報道原始代碼可在 Linux/ARM32(使用 GCC 編譯)和 Windows/AMD64(使用 msvc 編譯)上運行。

我的工作假設是,在所有這些運行時中, shared_ptr恰好將 object 指針作為第一個成員,並且編譯器決定通過引用傳遞它以避免復制(從而避免 incref/decref),因此make object 指針寫入Python 的p ,並將控制塊寫入空間(可能在堆棧的某個地方)。 當共享指針被釋放時,memory 仍然可以訪問(可能是因為它位於一個小的 object 池/頁面中,而不是未映射)。

然后get不需要觸及 refcount(因為 gain 由 ref 傳遞)所以它只是雙重取消引用我們的指針,這是一個 UAF 但 memory 仍然存在並且它可以解決。

注意:原版中沒有 UAF,因為 shared_ptr 是從一個壽命更長的結構中獲取的,所以這個簡化版比原版差了一點。

一些推測性事實:

  • shared_ptr 通常是 16 字節(在 64 位架構上),而 void* 是 8 字節。 shared_ptr 包含一個指向它所引用的 object 的指針,然后是另一個指向包含引用計數和析構函數的“控制塊”的指針。 (這只是 shared_ptr 的一種可能實現)

  • 覆蓋 memory 不一定會立即導致崩潰; 通常 memory 分配器分配的 memory 甚至比您要求的多(例如,它可能四舍五入為 16 字節的倍數)

  • 非平凡的 class 類型通過引用傳遞。 對真的。 就像你在類型之后寫的&一樣。 我不是在編造這個。 例如,許多 ABI 都基於Itanium ABI 為了模擬參數不是引用,調用者制作一個副本,然后在調用后銷毀它。 你沒有。

所以,可能:您打算將對shared_ptr的引用傳遞給make function。您做到了。 make function 用真實的shared_ptr覆蓋了它。 然后,不是像 ABI 應該那樣銷毀 object(從而使其假裝不是引用),而是將相同的引用傳遞給get function,它讀取在make中分配的新值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM