Android Native fdsan檢測工具介紹
正文
本文分析基于Android T(13)
fdsan,全名為file descriptor sanitizer,是Android中的一種檢測工具,用于檢測fd的use-after-close和double-close錯誤。這兩個錯誤會給設(shè)備留下安全漏洞,甚至造成數(shù)據(jù)泄露等安全問題。然而現(xiàn)實(shí)情況是這兩種錯誤非常隱蔽,且難以排查。這才催生了fdsan的誕生。
fdsan由Google的工程師Josh Gao開發(fā)。最初在Android 10中引入,檢測到錯誤時會打印log并繼續(xù)運(yùn)行。Android 11更改了運(yùn)行模式,一旦檢測到錯誤就立即abort,讓問題暴露得更加醒目。
要讓這些問題可以被檢測,關(guān)鍵是要構(gòu)建fd的所有權(quán)體系。
這套體系的建立有兩個重要前提:
- Android中大多數(shù)fd并非直接通過open/close的原生接口進(jìn)行管理,而是通過unique_fd,F(xiàn)ileOutputStream,ParcelFileDescriptor之類的封裝形式。
- fd是一個整數(shù),且每個進(jìn)程所能創(chuàng)建的fd都有上限,早期為1024,現(xiàn)在為32768。因此即便為每個fd創(chuàng)建單獨(dú)的所有權(quán)管理數(shù)據(jù)也不會消耗多少空間。
一個fd由生到死的所有者
基于這兩個條件,fdsan使用一個64-bit的tag來表明一個fd由生到死的所有者。
tag組成
tag由兩部分組成,最高位的8-bit構(gòu)成type,后面的56-bit構(gòu)成value。Type表示fd通過何種封裝形式進(jìn)行管理,譬如ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD
就表示fd通過unique_fd進(jìn)行管理。
對于應(yīng)用工程師而言,他們接觸到的大多為Java/Kotlin代碼,因此Java中常用的FileInputStream/FileOutputStream/ParcelFileDescriptor等封裝形式也有相應(yīng)的type。
詳細(xì)列表如下。
enum android_fdsan_owner_type { /* * Generic Java or native owners. * * Generic Java objects always use 255 as their type, using identityHashCode * as the value of the tag, leaving bits 33-56 unset. Native pointers are sign * extended from 48-bits of virtual address space, and so can have the MSB * set to 255 as well. Use the value of bits 49-56 to distinguish between * these cases. */ ANDROID_FDSAN_OWNER_TYPE_GENERIC_00 = 0, ANDROID_FDSAN_OWNER_TYPE_GENERIC_FF = 255, /* FILE* */ ANDROID_FDSAN_OWNER_TYPE_FILE = 1, /* DIR* */ ANDROID_FDSAN_OWNER_TYPE_DIR = 2, /* android::base::unique_fd */ ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD = 3, /* sqlite-owned file descriptors */ ANDROID_FDSAN_OWNER_TYPE_SQLITE = 4, /* java.io.FileInputStream */ ANDROID_FDSAN_OWNER_TYPE_FILEINPUTSTREAM = 5, /* java.io.FileOutputStream */ ANDROID_FDSAN_OWNER_TYPE_FILEOUTPUTSTREAM = 6, /* java.io.RandomAccessFile */ ANDROID_FDSAN_OWNER_TYPE_RANDOMACCESSFILE = 7, /* android.os.ParcelFileDescriptor */ ANDROID_FDSAN_OWNER_TYPE_PARCELFILEDESCRIPTOR = 8, /* ART FdFile */ ANDROID_FDSAN_OWNER_TYPE_ART_FDFILE = 9, /* java.net.DatagramSocketImpl */ ANDROID_FDSAN_OWNER_TYPE_DATAGRAMSOCKETIMPL = 10, /* java.net.SocketImpl */ ANDROID_FDSAN_OWNER_TYPE_SOCKETIMPL = 11, /* libziparchive's ZipArchive */ ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE = 12, };
tag中的value主要用作唯一性標(biāo)識。對于native層的封裝(譬如unique_fd)而言,value為封裝對象的指針值;對于Java層的封裝(譬如FileInputStream)而言,value為封裝對象的hash code。這兩種值在同一個進(jìn)程里都具有唯一性。
tag的創(chuàng)建和檢驗(yàn)過程
了解完tag的構(gòu)成后,接下來便是tag的創(chuàng)建和檢驗(yàn)過程。
通過封裝對象創(chuàng)建相應(yīng)fd時(通常在對象的構(gòu)造函數(shù)內(nèi)),tag便會創(chuàng)建。而所有的close都會去進(jìn)行tag檢驗(yàn),不論該close是通過封裝對象的析構(gòu)調(diào)用還是直接調(diào)用。舉個例子,當(dāng)一個fd通過FileInputStream創(chuàng)建時,對象回收期間便會close fd??墒侨绻覀儾恍⌒膶⒋薴d傳到了JNI函數(shù)中,通過close函數(shù)直接去操作它,那么檢測時我們就會發(fā)現(xiàn):期望的tag是一個非零值(由type和value構(gòu)成),實(shí)際的tag為0(通過原生close函數(shù)進(jìn)行操作時tag為0),繼而報(bào)錯。
典型的報(bào)錯log如下(與上述例子無關(guān)),一個通過opendir打開的fd,最終卻直接通過close關(guān)閉,而不是closedir。
pid: 610, tid: 610, name: lmkd >>> /system/bin/lmkd <<< uid: 1069 signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- Abort message: 'fdsan: attempted to close file descriptor 15, expected to be unowned, actually owned by DIR* 0x745b003c00' x0 0000000000000000 x1 0000000000000262 x2 0000000000000006 x3 0000007fc8696f90 x4 0000000000000000 x5 0000000000000000 x6 0000000000000000 x7 0000000000000010 x8 00000000000000f0 x9 643eeef5a527dc04 x10 0000000000000001 x11 0000000000000000 x12 0000000000000008 x13 0000000060345fc8 x14 000216096eb6f000 x15 000024dad07fc39e x16 000000745b715948 x17 000000745b6f4390 x18 000000745c0bc000 x19 0000000000000262 x20 0000000000000262 x21 000000745bc34000 x22 000000745cfdeb5c x23 0000000000000003 x24 0000007fc8697080 x25 ffffff80ffffffc8 x26 0000007fc8696d00 x27 0000007fc8696cc0 x28 0000000000000000 x29 0000007fc8697020 lr 000000745b6abf4c sp 0000007fc8696c40 pc 000000745b6abf6c pst 0000000000001000 backtrace: #00 pc 000000000008df6c /apex/com.android.runtime/lib64/bionic/libc.so (fdsan_error(char const*, ...)+588) #01 pc 000000000008dc68 /apex/com.android.runtime/lib64/bionic/libc.so (android_fdsan_close_with_tag+740) #02 pc 000000000008e3d0 /apex/com.android.runtime/lib64/bionic/libc.so (close+16) #03 pc 000000000000b110 /system/bin/lmkd (start_wait_for_proc_kill(int)+184) #04 pc 000000000000a45c /system/bin/lmkd (find_and_kill_process(int, int, char const*, meminfo*, timespec*, bool)+744) #05 pc 00000000000098a0 /system/bin/lmkd (mp_event_common(int, unsigned int, polling_params*)+2140) #06 pc 000000000000ba1c /system/bin/lmkd (call_handler(event_handler_info*, polling_params*, unsigned int)+64) #07 pc 00000000000053a8 /system/bin/lmkd (main+2440) #08 pc 000000000008506c /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108)
且慢,討論了這么久的tag到底存在什么地方?可以肯定的是存在一個全局的table中,由于fd是一個32768以內(nèi)的整數(shù),因此可以將fd作為table的索引。此外,Josh將這個table設(shè)計(jì)成可擴(kuò)容的,初始大小為128,擴(kuò)容后可以存放下所有fd的tag。這樣對于大多數(shù)fd創(chuàng)建數(shù)量較少的進(jìn)程,便可以使用較小的table來節(jié)省內(nèi)存。
fdsan自引入之際便全局打開,對于保障系統(tǒng)的安全至關(guān)重要。但受限于它的原理,仍然有一些fd的use-after-close和double-close錯誤無法被檢出。它們是:
- 不使用任何封裝形式,而只是通過open和close來操作fd。這樣即便對同一個fd double-close,由于期望的tag和實(shí)際的tag都為0,因此錯誤無法檢出。
- 使用自定義的封裝形式來操作fd,而沒有在自定義的形式中接入fdsan。無法檢出的理由同上。
- 單純的use-after-close,而并非由double-close引發(fā)的use-after-close也無法檢出。嚴(yán)格意義上來說,我認(rèn)為這個工具只能檢出fd的double-close。原因是檢測代碼只插入在close函數(shù)里,對于fd的其他操作并無檢測。
除了檢測fd的錯誤外,存在全局table中的tag數(shù)據(jù)在生成tombstone時也會輸出,表示每個fd的ownership。unowned
表明該fd的創(chuàng)建未接入fdsan。
open files: ... fd 47: /data/app/~~TPUHOtvHOT-iL2f4uUrFSg==/com.tencent.mm-FKuS1oTvzRpPMOXX-SE_ZQ==/base.apk (owned by ZipArchive 0xfffd2ae7dd90) fd 48: /data/app/~~TPUHOtvHOT-iL2f4uUrFSg==/com.tencent.mm-FKuS1oTvzRpPMOXX-SE_ZQ==/split_config.arm64_v8a.apk (owned by ZipArchive 0xfffd2ae82390) fd 49: /data/app/~~TPUHOtvHOT-iL2f4uUrFSg==/com.tencent.mm-FKuS1oTvzRpPMOXX-SE_ZQ==/split_config.en.apk (owned by ZipArchive 0xfffd2ae83ac0) fd 50: /data/app/~~TPUHOtvHOT-iL2f4uUrFSg==/com.tencent.mm-FKuS1oTvzRpPMOXX-SE_ZQ==/split_config.hdpi.apk (owned by ZipArchive 0xfffd2ae84e00) fd 51: /data/data/com.tencent.mm/files/splitcompat/2103/verified-splits/delivery.config.arm64_v8a.apk (owned by ZipArchive 0xfffd2ae80b10) fd 52: /data/data/com.tencent.mm/files/splitcompat/2103/verified-splits/delivery.apk (owned by ZipArchive 0xfffd2ae803a0) fd 53: anon_inode:[eventfd] (owned by unique_fd 0xfffd7ae85e84) fd 54: anon_inode:[eventpoll] (owned by unique_fd 0xfffd7ae85edc) fd 55: anon_inode:[eventfd] (owned by unique_fd 0xfffd7ae99ce4) fd 56: anon_inode:[eventpoll] (owned by unique_fd 0xfffd7ae99d3c) fd 57: /data/app/~~TPUHOtvHOT-iL2f4uUrFSg==/com.tencent.mm-FKuS1oTvzRpPMOXX-SE_ZQ==/base.apk (unowned) fd 58: /data/app/~~TPUHOtvHOT-iL2f4uUrFSg==/com.tencent.mm-FKuS1oTvzRpPMOXX-SE_ZQ==/split_config.arm64_v8a.apk (unowned)
最后值得注意的一點(diǎn)是,fdsan在vfork得到的子進(jìn)程里是不工作的。原因是vfork得到的子進(jìn)程雖然會拷貝父進(jìn)程的fd,但是使用的地址空間仍然屬于父進(jìn)程。因此fd和存儲fd tag的內(nèi)存產(chǎn)生了錯配,在子進(jìn)程中操作fd不會被父進(jìn)程感知到,而tag的修改則會直接影響父進(jìn)程的table。
【參考文檔】 android.googlesource.com/platform/bi…
以上就是Android Native fdsan檢測工具介紹的詳細(xì)內(nèi)容,更多關(guān)于Android Native fdsan檢測工具的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android RichText 讓Textview輕松的支持富文本(圖像ImageSpan、點(diǎn)擊效果等等類似QQ微信聊
AndroidRichText幫助實(shí)現(xiàn)像QQ,微信一樣的,一個TextView里既有文字又有表情又有圖片的效果,采用插件化的框架,代碼簡單,可拓展性強(qiáng)2016-01-01Android音視頻開發(fā)Media FrameWork框架源碼解析
這篇文章主要為大家介紹了Android音視頻開發(fā)Media FrameWork框架源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12獲取Android應(yīng)用專屬緩存存儲目錄的實(shí)例
今天小編就為大家分享一篇獲取Android應(yīng)用專屬緩存存儲目錄的實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08Android模糊處理實(shí)現(xiàn)圖片毛玻璃效果
這篇文章主要介紹了Android模糊處理實(shí)現(xiàn)圖片毛玻璃效果,需要的朋友可以參考下2016-02-02