[英]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.