動態記憶體管理

釋放後使用(use-after-free)

你還了一本圖書館的書,隔天卻出於習慣在書的頁邊寫了個註記——只是這本書已不再是你的,而且別人已經借走了。你的註記現在落進了陌生人的書裡。釋放後使用就是這件事的程式版:你對某塊區塊呼叫 free(),然後又透過一個仍指向那塊已釋放區塊的指標去讀或寫。該記憶體可能早已被交給另一次 malloc、裝著完全不同的東西。

精確地說:free(p) 之後,指標 p 是一個懸置指標——它仍持有舊位址,但配置器現在認為那塊記憶體可用、可能把它重用於下一個配置請求。之後透過 p 讀取會得到當下恰好在那裡的東西(也許僥倖是舊資料,也許是另一個物件的位元組)。透過 p 寫入則會破壞現在占用那塊記憶體的東西。兩者都是未定義行為:C 標準對其後果不作任何要求,所以程式可能看似正常、可能立即當機,也可能默默破壞資料、稍後在某個毫不相干的地方才當掉。錯誤與症狀之間的這段延遲,正是釋放後使用成為最難追查之錯誤之一的原因。

從安全角度看它也是最危險的之一。攻擊者利用釋放後使用的方法,是設法讓一個他們控制的配置落進那塊已釋放的區塊,使得那個過期的指標現在讀寫到攻擊者選定的資料——這是接管程式的常見途徑。防禦靠的是紀律:釋放後立刻把指標設為 NULL(讓誤用變成一次乾淨的 NULL 解參考,較易抓出)、保持所有權清晰以免釋放了程式其他部分仍依賴的記憶體,並執行像 AddressSanitizer 或 Valgrind 這類能偵測對已釋放區塊之存取的工具。

char *p = malloc(16); free(p); p[0] = 'x'; /* 釋放後使用:寫入已釋放的記憶體(未定義行為) */ /* 較安全:free(p); p = NULL; 之後 p[0] = 'x' 是乾淨的 NULL 解參考,易於發現 */

free 之後 p 是懸置的;碰它是未定義行為。把 p 設為 NULL 能把無聲的破壞變成明顯的當機。

它可能看似正常,因為已釋放的位元組仍保有舊值——那是僥倖,不是正確。一旦配置器重用該區塊,同樣的程式碼就會破壞資料或當機。

又称
UAFdangling-pointer use懸置指標存取