本頁說明 Android 如何處理平台無線 (OTA) 更新的政策相容性問題,因為新的平台 SELinux 設定可能與舊的供應商 SELinux 設定不同。
物件擁有權和標籤
每個物件都必須明確定義擁有權,才能將平台和供應商政策分開。舉例來說,如果供應商政策標籤 /dev/foo 和平台政策標籤 /dev/foo 在後續的 OTA 中,出現未定義的行為 (例如意外拒絕),或更嚴重的是啟動失敗,在 SELinux 中,這會顯示為標籤衝突。裝置節點只能有一個標籤,該標籤會解析為最後套用的標籤。因此:
- 需要存取未成功套用標籤的程序會失去資源存取權。
- 取得檔案存取權的程序可能會中斷,因為建立的裝置節點有誤。
凡是具有 SELinux 標籤的物件 (包括屬性、服務、程序、檔案和通訊端),都可能發生平台與供應商標籤之間的衝突。為避免發生這些問題,請清楚定義這些物件的擁有權。
型別/屬性命名空間
除了標籤衝突外,SELinux 類型和屬性名稱也可能發生衝突。SELinux 不允許重複宣告相同型別和屬性。如果政策含有重複的聲明,編譯就會失敗。為避免型別和屬性名稱發生衝突,強烈建議所有供應商宣告都以 vendor_ 前置字元開頭。舉例來說,供應商應使用 type vendor_foo, domain;,而非 type foo, domain;。
檔案擁有權
由於平台和供應商政策通常會為所有檔案系統提供標籤,因此要避免檔案發生衝突並不容易。與型別命名不同,檔案的命名空間並不實用,因為其中許多檔案是由核心建立。為避免發生這類衝突,請按照本節的檔案系統命名指引操作。如果是 Android 8.0,這些建議不會強制執行。日後,供應商測試套件 (VTS) 將會強制執行這些建議。
系統 (/system)
只有系統映像檔必須透過 file_contexts、service_contexts 等為 /system 元件提供標籤。如果供應商政策中新增了 /system 元件的標籤,可能無法進行僅限架構的 OTA 更新。
供應商 (/vendor)
AOSP SELinux 政策已標記平台互動的 vendor 分區部分,因此可為平台程序編寫 SELinux 規則,以便與 vendor 分區部分通訊或存取這些部分。範例:
| /vendor path | 平台提供的標籤 | 平台程序 (視標籤而定) |
|---|---|---|
/vendor(/.*)?
|
vendor_file
|
架構中的所有 HAL 用戶端,ueventd 等。
|
/vendor/framework(/.*)?
|
vendor_framework_file
|
dex2oat、appdomain 等
|
/vendor/app(/.*)?
|
vendor_app_file
|
dex2oat、installd、idmap 等。
|
/vendor/overlay(/.*)
|
vendor_overlay_file
|
system_server、zygote、idmap 等。
|
因此,在 vendor 分區中標記其他檔案時,必須遵守特定規則 (透過 neverallows 強制執行):
vendor_file必須是vendor分區中所有檔案的預設標籤。平台政策規定必須這樣做,才能存取 HAL 實作的直通功能。- 透過供應商政策在
vendor分區中新增的所有exec_types都必須具備vendor_file_type屬性。這項限制會透過 neverallow 強制執行。 - 為避免日後與平台/架構更新發生衝突,請勿在
vendor分割區中標示exec_types以外的檔案。 - 針對 AOSP 識別的相同程序 HAL,所有程式庫依附元件都必須標示為
same_process_hal_file.
Procfs (/proc)
/proc 中的檔案只能使用 genfscon 標籤標示。在 Android 7.0 中,平台和供應商政策都使用 genfscon 為 procfs 中的檔案加上標籤。
建議:僅限平台政策標籤 /proc。
如果供應商程序需要存取 /proc 中目前標示預設標籤 (proc) 的檔案,供應商政策不應明確標示這些檔案,而應使用一般 proc 類型,為供應商網域新增規則。這樣一來,平台更新就能配合日後透過 procfs 公開的 Kernel 介面,並視需要明確標示。
Debugfs (/sys/kernel/debug)
Debugfs 可在 file_contexts 和 genfscon 中加上標籤。在 Android 7.0 至 Android 10 中,平台和供應商標籤都會顯示。debugfs
在 Android 11 中,debugfs 無法在正式版裝置上存取或掛接。裝置製造商應移除 debugfs。
Tracefs (/sys/kernel/debug/tracing)
Tracefs 可在 file_contexts 和 genfscon 中加上標籤。在 Android 7.0 中,只有平台標籤
tracefs。
建議:只有平台可以標示 tracefs。
Sysfs (/sys)
/sys 中的檔案可能會同時使用 file_contexts 和 genfscon 加上標籤。在 Android 7.0 中,平台和供應商都會使用 genfscon 為 sysfs 中的檔案加上標籤。
建議:平台可能會標示非裝置專用的 sysfs 節點。否則只有供應商可以標記檔案。
tmpfs (/dev)
/dev 中的檔案可能會在 file_contexts 中加上標籤。在 Android 7.0 中,平台和供應商標籤檔案都位於此處。
最佳做法:供應商只能標記 /dev/vendor 中的檔案 (例如 /dev/vendor/foo、/dev/vendor/socket/bar)。
Rootfs (/)
/ 中的檔案可能會在 file_contexts 中加上標籤。在 Android 7.0 中,平台和供應商標籤檔案都位於此處。
建議:只有系統可以在 / 中標示檔案。
資料 (/data)
資料標示方式是結合 file_contexts 和 seapp_contexts。
最佳化建議:禁止在外部使用供應商標籤
/data/vendor。只有平台可以標記其他部分的
/data。
Genfs 標籤版本
自供應商 API 級別 202504 起,較舊的 vendor 分割區可選擇是否要指派以 genfscon 標示的較新 SELinux 標籤。system/sepolicy/compat/plat_sepolicy_genfs_ver.cil這樣一來,舊版vendor分割區就能保留現有的 SEPolicy 實作項目。這項設定由 Makefile 變數 BOARD_GENFS_LABELS_VERSION 控制,該變數儲存在 /vendor/etc/selinux/genfs_labels_version.txt 中。
範例:
-
在供應商 API 級別 202404 中,
/sys/class/udc節點預設標示為sysfs。 -
從供應商 API 級別 202504 開始,
/sys/class/udc會標示為sysfs_udc。
不過,/sys/class/udc 可能會由使用 API 級別 202404 的vendor分區使用,無論是使用預設的 sysfs 標籤或供應商專屬標籤。無條件將 /sys/class/udc 標示為 sysfs_udc 可能會導致與這些 vendor 分區不相容。勾選 BOARD_GENFS_LABELS_VERSION 後,平台會繼續使用舊版 vendor 分區的標籤和權限。
BOARD_GENFS_LABELS_VERSION 可以大於或等於供應商 API 級別。舉例來說,使用 API 級別 202404 的 vendor 分區可以將 BOARD_GENFS_LABELS_VERSION 設為 202504,採用 202504 推出的新標籤。請參閱
202504 專屬 genfs 標籤清單。
標記 genfscon 節點時,平台必須考量舊版 vendor 分區,並視需要導入相容性備援機制。平台可以使用平台專屬程式庫查詢 genfs 標籤版本。
-
在原生平台,請使用
libgenfslabelsversion。如需libgenfslabelsversion的標頭檔案,請參閱genfslabelsversion.h。 -
在 Java 中,請使用
android.os.SELinux.getGenfsLabelsVersion()。
平台公共政策
平台 SELinux 政策分為私有和公開。平台公開政策包含的類型和屬性一律適用於供應商 API 級別,可做為平台和供應商之間的 API。這項政策會向供應商政策撰寫者公開,讓供應商建構供應商政策檔案,並與平台專屬政策合併,為裝置產生完整政策。平台公開政策定義於 system/sepolicy/public。
舉例來說,在供應商的環境中,代表初始化程序的 vendor_init 類型定義於 system/sepolicy/public/vendor_init.te 下方:
type vendor_init, domain;
供應商可以參考 vendor_init 類型,編寫自訂政策規則:
# Allow vendor_init to set vendor_audio_prop in vendor's init scripts
set_prop(vendor_init, vendor_audio_prop)相容性屬性
SELinux 政策是來源和目標類型之間的互動,適用於特定物件類別和權限。受 SELinux 政策影響的每個物件 (例如程序、檔案) 只能有一種型別,但該型別可能有多個屬性。
這項政策主要以現有類型編寫,這裡的 vendor_init 和 debugfs 都是型別:
allow vendor_init debugfs:dir { mounton };
這是因為政策是根據所有型別編寫而成。不過,如果供應商政策和平台政策使用特定類型,且特定物件的標籤只在其中一項政策中變更,另一項政策可能包含先前取得或失去存取權的政策。舉例來說,假設平台政策將 sysfs 節點標示為 sysfs:
/sys(/.*)? u:object_r:sysfs:s0
供應商政策會授予 /sys/usb 的存取權,標示為:
sysfs:
allow vendor_init sysfs:chr_file rw_file_perms;
如果平台政策變更為將 /sys/usb 標示為 sysfs_usb,供應商政策會維持不變,但由於缺少新 sysfs_usb 類型的政策,vendor_init 會失去 /sys/usb 的存取權:
/sys/usb u:object_r:sysfs_usb:s0
為解決這個問題,Android 導入了版本化屬性的概念。在編譯時間,建構系統會自動將供應商政策中使用的平台公開型別,轉換為這些已加入版本的屬性。這項翻譯功能會透過對應檔案啟用,將已加上版本的屬性與平台的一或多個公開型別建立關聯。
舉例來說,假設 /sys/usb 標示為 sysfs,且 2025 年 4 月的平台政策和供應商政策都授予 vendor_init 存取 /sys/usb 的權限。在這種情況下:
-
供應商政策會寫入規則
allow vendor_init sysfs:chr_file rw_file_perms;,因為/sys/usb標示為 202504 平台政策中的sysfs。建構系統編譯供應商政策時,會自動將規則轉換為allow vendor_init_202504 sysfs_202504:chr_file rw_file_perms;。屬性vendor_init_202504和sysfs_202504分別對應至vendor_init和sysfs類型,這些是平台定義的類型。 -
建構系統會產生識別資訊對應檔
/system/etc/selinux/mapping/202504.cil。由於system和vendor分區都使用相同的202504版本,因此對應檔案包含從type_202504到type的身分對應。舉例來說,vendor_init_202504對應至vendor_init,sysfs_202504對應至sysfs:(typeattributeset sysfs_202504 (sysfs)) (typeattributeset vendor_init_202504 (vendor_init)) ...
當版本從 202504 升級至 202604 時,系統會在 system/sepolicy/private/compat/202504/202504.cil 下建立 202504 vendor 分區的新對應檔案,並安裝至 202604 或更新版本的 system 分區 /system/etc/selinux/mapping/202504.cil。如先前所述,這個對應檔案一開始會包含身分對應。如果 202604 平台政策新增 sysfs_usb
的 /sys/usb 標籤,對應檔案會更新,將 sysfs_202504 對應至 sysfs_usb:
(typeattributeset sysfs_202504 (sysfs sysfs_usb)) (typeattributeset vendor_init_202504 (vendor_init)) ...
這項更新可讓轉換後的供應商政策規則 allow
vendor_init_202504 sysfs_202504:chr_file rw_file_perms; 自動授予 vendor_init 新 sysfs_usb 類型的存取權。
為維持與舊版 vendor 分區的相容性,每當新增公開型別時,該型別必須對應至對應檔案 system/sepolicy/private/compat/ver/ver.cil 中的至少一個版本化屬性,或列於 system/sepolicy/private/compat/ver/ver.ignore.cil 下方,說明先前供應商版本中沒有相符的型別。
平台政策、供應商政策和對應檔案的組合,可讓系統在不更新供應商政策的情況下進行更新。此外,系統會自動將屬性轉換為版本化屬性,因此供應商政策不必處理版本化作業,可繼續使用公開型別。
system_ext 公開和產品公開政策
從 Android 11 開始,system_ext 和 product 分區可將指定公開型別匯出至 vendor 分區。與平台公開政策相同,供應商政策會使用自動轉換為版本化屬性的類型和規則,例如從 type 轉換為 type_ver,其中 ver 是 vendor 分區的供應商 API 級別。
如果 system_ext 和 product 分區是根據相同平台版本 ver,建構系統會產生基本對應檔案至 system_ext/etc/selinux/mapping/ver.cil 和 product/etc/selinux/mapping/ver.cil,其中包含從 type 到 type_ver 的身分對應。供應商政策可透過版本化屬性 type_ver 存取 type。
如果只有 system_ext 和 product 分區更新 (例如從 ver 更新至 ver+1 或更新版本),而 vendor 分區仍為 ver,供應商政策可能會失去 system_ext 和 product 分區的存取權。為避免中斷,system_ext 和 product 分區應提供從具體型別到 type_ver 屬性的對應檔案。如果合作夥伴支援 ver vendor 分區 (使用 ver+1 或更新版本 system_ext 和 product 分區),則須負責維護對應檔案。
如要將對應檔案安裝至 system_ext 和 product 分區,裝置實作人員或供應商應採取下列行動:
- 將產生的基礎對應檔案從 ver
system_ext和product分區 複製到來源樹狀結構。 - 視需要修改對應檔案。
-
將對應檔案安裝至 ver+1 (或更新版本) 和
product分割區。system_ext
舉例來說,假設 202504 system_ext 分區有一個名為 foo_type 的公開型別。然後 202504 system_ext 分區中的 system_ext/etc/selinux/mapping/202504.cil 看起來會像這樣:
(typeattributeset foo_type_202504 (foo_type)) (expandtypeattribute foo_type_202504 true) (typeattribute foo_type_202504)
如果 bar_type 已新增至 202604 system_ext,且 bar_type 應對應至 202504 foo_type 分區,則 202504.cil 可從 (typeattributeset foo_type_202504 (foo_type)) 更新至 (typeattributeset foo_type_202504 (foo_type bar_type)),然後安裝至 202604 system_ext 分區。vendor202504 vendor 分區可以繼續存取 202604 system_ext 的 foo_type 和 bar_type。
Android 9 的屬性變更
升級至 Android 9 的裝置可以使用下列屬性,但搭載 Android 9 的裝置不得使用。
違規者屬性
Android 9 包含下列網域相關屬性:
data_between_core_and_vendor_violators. 所有網域的屬性,這些網域違反不得在vendor和coredomains之間透過路徑共用檔案的規定。平台和供應商程序不應使用磁碟上的檔案進行通訊 (ABI 不穩定)。建議:- 供應商代碼應使用
/data/vendor。 - 系統不應使用
/data/vendor。
- 供應商代碼應使用
system_executes_vendor_violators。所有系統網域 (init和shell domains除外) 的屬性,都不得違反不得執行供應商二進位檔的規定。執行供應商二進位檔時,API 不穩定。平台不應直接執行供應商二進位檔。建議:- 這類平台對供應商二進位檔的依附元件必須位於 HIDL HAL 後方。
或
coredomains,需要存取供應商二進位檔的coredomains應移至vendor分區,並停止成為coredomain。
- 這類平台對供應商二進位檔的依附元件必須位於 HIDL HAL 後方。
不受信任的屬性
代管任意程式碼的不受信任應用程式不應存取 HwBinder 服務,但可存取被視為足夠安全,可供這類應用程式存取的服務 (請參閱下方的安全服務)。主要原因有二:
- HwBinder 伺服器不會執行用戶端驗證,因為 HIDL 目前不會公開呼叫端 UID 資訊。即使 HIDL 確實公開這類資料,許多 HwBinder 服務不是在應用程式層級以下運作 (例如 HAL),就是不得依據應用程式 ID 進行授權。因此,為求安全,預設假設是每個 HwBinder 服務都會將所有用戶端視為同樣有權執行服務提供的作業。
- HAL 伺服器 (HwBinder 服務的子集) 包含的程式碼,安全問題發生率高於
system/core元件,且可存取堆疊的較低層 (一路到硬體),因此增加規避 Android 安全性模型的機會。
安全服務
安全服務包括:
same_process_hwservice。這些服務 (根據定義) 會在用戶端程序中執行,因此與程序執行的用戶端網域具有相同的存取權。coredomain_hwservice。這些服務不會造成與原因 #2 相關的風險。hal_configstore_ISurfaceFlingerConfigs。這項服務專為任何網域設計。hal_graphics_allocator_hwservice。這些作業也由surfaceflingerBinder 服務提供,應用程式可存取該服務。hal_omx_hwservice。這是mediacodecBinder 服務的 HwBinder 版本,應用程式可存取此服務。hal_codec2_hwservice。這是hal_omx_hwservice的新版本。
可用的屬性
所有不安全的 hwservices 都會具有 untrusted_app_visible_hwservice 屬性。對應的 HAL 伺服器具有 untrusted_app_visible_halserver 屬性。搭載 Android 9 的裝置「不得」使用任一 untrusted 屬性。
建議:
- 不信任的應用程式應改為與系統服務通訊,再由系統服務與供應商 HIDL HAL 通訊。舉例來說,應用程式可以與
binderservicedomain對話,然後mediaserver(也就是binderservicedomain) 會與hal_graphics_allocator對話。或
- 需要直接存取
vendorHAL 的應用程式應有自己的供應商定義 sepolicy 網域。
檔案屬性測試
Android 9 包含建構時間測試,可確保特定位置的所有檔案都具有適當的屬性 (例如 sysfs 中的所有檔案都具有必要的 sysfs_type 屬性)。
SELinux 內容標籤
為支援平台和供應商 sepolicy 的區別,系統會以不同方式建構 SELinux 內容檔案,確保兩者分開。
檔案內容
Android 8.0 針對 file_contexts 導入下列變更:
- 為避免在啟動期間裝置上產生額外的編譯負擔,
file_contexts不會以二進位形式存在。而是可讀取的規則運算式文字檔,例如{property, service}_contexts(7.0 之前的版本就是如此)。 file_contexts分成兩個檔案:plat_file_contexts- Android 平台
file_context沒有裝置專屬標籤,但/vendor分割區的部分必須精確標示,確保 sepolicy 檔案正常運作。 - 必須位於裝置的
system分割區,並在啟動時由init連同供應商file_context一併載入。/system/etc/selinux/plat_file_contexts
- Android 平台
vendor_file_contexts- 裝置專屬
file_context是透過合併裝置Boardconfig.mk檔案中BOARD_SEPOLICY_DIRS所指向目錄中的file_contexts所建構。 - 必須安裝在
vendor分割區的/vendor/etc/selinux/vendor_file_contexts中,並在啟動時由init與平台file_context一併載入。
- 裝置專屬
屬性環境
在 Android 8.0 中,property_contexts 分成兩個檔案:
plat_property_contexts- Android 平台
property_context沒有裝置專屬標籤。 - 必須位於
system分區的/system/etc/selinux/plat_property_contexts,並在啟動時由init與供應商property_contexts一併載入。
- Android 平台
vendor_property_contexts- 裝置專屬
property_context是透過合併裝置Boardconfig.mk檔案中BOARD_SEPOLICY_DIRS所指向目錄中的property_contexts所建構。 - 必須位於
vendor分區的/vendor/etc/selinux/vendor_property_contexts,並在啟動時由init與平台property_context一併載入
- 裝置專屬
服務環境
在 Android 8.0 中,service_contexts 會拆分成下列檔案:
plat_service_contexts- Android 平台專屬的
service_context,適用於servicemanager。service_context沒有裝置專屬標籤。 - 必須位於
system分區的/system/etc/selinux/plat_service_contexts,並在啟動時由servicemanager與供應商service_contexts一併載入。
- Android 平台專屬的
vendor_service_contexts- 裝置專屬
service_context是透過合併裝置Boardconfig.mk檔案中BOARD_SEPOLICY_DIRS所指向目錄中的service_contexts所建構。 - 必須位於
vendor分區的/vendor/etc/selinux/vendor_service_contexts,並在啟動時由servicemanager與平台service_contexts一併載入。 - 雖然
servicemanager會在啟動時尋找這個檔案,但如要讓裝置完全符合TREBLE規範,就「不得」存在vendor_service_contexts。這是因為vendor和system程序之間的所有互動都必須經過hwservicemanager/hwbinder。
- 裝置專屬
plat_hwservice_contexts- Android 平台
hwservice_context,沒有裝置專屬標籤。hwservicemanager - 必須位於
system分區的/system/etc/selinux/plat_hwservice_contexts,並在啟動時與vendor_hwservice_contexts一併由hwservicemanager載入。
- Android 平台
vendor_hwservice_contexts- 裝置專屬
hwservice_context是透過合併裝置Boardconfig.mk檔案中BOARD_SEPOLICY_DIRS所指向目錄中的hwservice_contexts所建構。 - 必須位於
vendor分區的/vendor/etc/selinux/vendor_hwservice_contexts,並由hwservicemanager在啟動時與plat_service_contexts一併載入。
- 裝置專屬
vndservice_contexts- 裝置專屬
service_context,是透過合併裝置Boardconfig.mk中BOARD_SEPOLICY_DIRS所指向目錄中的vndservice_contexts所建構。vndservicemanager - 這個檔案必須位於
vendor分區的/vendor/etc/selinux/vndservice_contexts,並在啟動時由vndservicemanager載入。
- 裝置專屬
Seapp 內容
在 Android 8.0 中,seapp_contexts 分成兩個檔案:
plat_seapp_contexts- Android 平台
seapp_context,且沒有裝置專屬變更。 - 必須位於
system分區,且位於/system/etc/selinux/plat_seapp_contexts.
- Android 平台
vendor_seapp_contexts- 平台
seapp_context的裝置專屬擴充功能,是透過合併裝置Boardconfig.mk檔案中BOARD_SEPOLICY_DIRS所指向目錄的seapp_contexts所建構。 - 必須位於
vendor分區的/vendor/etc/selinux/vendor_seapp_contexts。
- 平台
MAC 權限
在 Android 8.0 中,mac_permissions.xml 分成兩個檔案:
- 月台
mac_permissions.xml- Android 平台
mac_permissions.xml,且沒有裝置專屬變更。 - 必須位於
system分區,且位於/system/etc/selinux/.
- Android 平台
- 非平台
mac_permissions.xml- 平台專屬的裝置擴充功能,由裝置的
Boardconfig.mk檔案中BOARD_SEPOLICY_DIRS所指向目錄的mac_permissions.xml建構而成。mac_permissions.xml - 必須位於
vendor分區,且位於/vendor/etc/selinux/.
- 平台專屬的裝置擴充功能,由裝置的
Android 17 的共用記憶體變更
從 Android 17 開始,如果裝置具備下列屬性,就必須啟用 memfd_class 政策功能,並更新共用記憶體相關政策,以支援 memfd_file 類別物件:
- 供應商 API 級別 202604 以上,讓供應商和原始設備製造商有機會更新供應商政策,以支援
memfd。此外,現有裝置也能升級至較高版本的 Android,不必更新供應商分割區。 android16-6.12以上版本,因為這些核心支援memfd_class功能,而實作memfd的精細政策需要這項功能。
啟用 memfd_class 政策功能
直到最近,SELinux 仍將 memfd 標示為與其支援檔案系統 (tmpfs) 類型相同的檔案。從政策角度來看,這使得 tmpfs 掛接上的 memfd 無法與其他檔案區別。現在,SELinux 會使用分配程序的安全情境標記 memfd,且 memfds 會視為 memfd_file 類別物件。這項功能受到 memfd_class 政策功能的保護,可保留與舊版使用者空間環境的向後相容性。
如要啟用 memfd_class 政策功能,請在 BOARD_VENDOR_SEPOLICY_DIRS 下建立 policy_capabilities 檔案。檔案應包含下列項目:
# $BOARD_VENDOR_SEPOLICY_DIRS/*/policy_capabilities
policycap memfd_class;接著重建映像檔並刷入裝置,確認這項功能已啟用。
確認已啟用 memfd_class 政策功能
使用下列指令檢查 memfd_class 政策功能狀態:
adb shell 'cat /sys/fs/selinux/policy_capabilities/memfd_class'
如果結果為 1,則會啟用 memfd_class 政策功能。否則不會啟用。
將現有政策轉換為 memfd
某些程序會在政策中使用 tmpfs_domain() 巨集存取及命名 memfds,例如:
# foo.te
tmpfs_domain(foo)這表示:
# foo.te type_transition foo tmpfs:file foo_tmpfs; allow foo foo_tmpfs:file { read write getattr map };
並允許程序 bar 存取程序 foo 的 memfds,如下所示:
# bar.te allow bar foo_tmpfs:file { read write getattr map };
啟用 memfd_class 政策功能後,就不再需要 tmpfs_domain() 巨集,因為平台政策已更新,允許任何程序建立及使用自己的 memfds,如下所示:
# system/sepolicy/private/domain.te allow domain self:memfd_file { create read write getattr map };
程序 foo 建立的 memfds 可由程序 bar 存取,如下所示:
# bar.te allow bar foo:memfd_file { read write getattr map };
平台政策已更新,將現有 memfd 用法納入考量。不過,使用 tmpfs 標籤的廠商和裝置專屬政策必須更新為使用 memfd_file。如果政策是在沒有供應商 API 級別 202604 以上的 SoC 或裝置之間共用,建議保留舊版 tmpfs 政策,並搭配新版 memfd_file 政策,以確保相容性。
找出與 memfd 相關的 AVC 拒絕事項
您可以使用下列指令,擷取與 Memfd 相關的拒絕要求:
adb shell logcat -d -b events | grep memfd
以 tmpfs 為目標的 avc 拒絕
以下範例顯示程序嘗試寫入沒有寫入權限的 memfd 時,遇到的 avc 拒絕要求:
audit(0.0:539): avc: denied { write } for comm="binder:665_1" name="memfd:MessageQueue"
dev="tmpfs" ino=8324 scontext=u:r:mediacodec:s0 tcontext=u:object_r:tmpfs:s0 tclass=file
permissive=0
啟用 memfd_class 政策功能後,memfd 的目標內容是分配程序的安全內容,而非 tmpfs,目標類別是 memfd_file,而非 file。因此,如果您發現avc遭到與memfd相關的拒絕,且相關memfd標示為tmpfs檔案,則表示memfd_class政策功能未啟用。
以 memfd_file 為目標類別的 AVC 拒絕
以下範例顯示程序嘗試寫入沒有寫入權限的 memfd 時,遇到的 avc 拒絕,以及啟用 memfd_class 政策功能後,logd 在拒絕後發出的額外行,兩者具有相同時間戳記:
audit(0.0:86): avc: denied { read } for
path=2F6D656D66643A4D6564696142756666657247726F7570202864656C6574656429 ino=512 dev=""
scontext=u:r:mediaserver:s0 tcontext=u:object_r:mediaextractor:s0 tclass=memfd_file
auditd : Decoded path for audit(0.0:86): /memfd:MediaBufferGroup (deleted)
相符的時間戳記表示 Decoded path for … log 與 avc 拒絕有關,且時間戳記為 0.0.86。這份記錄會解碼 avc 拒絕中的路徑值 (十六進位字串),並提供 memfd 記憶體區域的名稱,有助於瞭解共用緩衝區。來源內容和目標內容有助於瞭解哪些程序需要共用記憶體。從上述範例可清楚看出,mediaserver 程序必須能夠存取 mediaextractor 的 memfds。因此,適用的政策為:
# mediaserver.te allow mediaserver mediaextractor:memfd_file { getattr read write map };
Android 17 的安全網域更新
Android 17 中的 ASharedMemory_create() API 會實作條件邏輯,以便在舊版 ashmem 驅動程式和 memfd 架構之間選擇,用於共用記憶體配置。
如果裝置符合 memfd 需求 (供應商 API 級別 202604 以上,且核心 android16-6.12 以上),API 會評估呼叫應用程式的 targetSdkVersion。如果目標 SDK 版本為 37 以上,系統會分配 memfd。開發人員升級目標 SDK 版本時,可以藉此修正遇到的問題。
如果裝置不符合 memfd's 先決條件,ASharedMemory 會改回使用 ashmem。這樣一來,升級後的裝置就能繼續與舊版供應商分割區或核心相容。
為強制執行這項轉換,平台 SELinux 政策會禁止以 SDK 版本 37 以上為目標的應用程式,在 platform_app、priv_app 和 untrusted_app 安全性網域中開啟 /dev/ashmem,以及在 memfd 上叫用 ashmem ioctl 指令。方法是根據目標 SDK 版本分割這些應用程式網域。這項變更會導入 platform_app_36、priv_app_36 和 untrusted_app_34 安全性網域,這些網域與其他應用程式網域會保留 ashmem 開啟權限,並能在 memfds 上叫用 ashmem ioctl 指令。
在日後的 Android 版本中,保留在 memfds 上開啟 ashmem 裝置及叫用 ashmem ioctl 指令權限的應用程式集,將縮減為僅限 platform_app_36、priv_app_36 和 untrusted_app_34,以及舊版 SDK 的不受信任應用程式網域。
如要配合這些網域變更,應用程式的自訂供應商或 OEM SELinux 政策必須更新,詳情請參閱下列章節。
平台應用程式 SELinux 網域更新
platform_app 網域會根據應用程式的 targetSdkVersion 分割。指定 SDK 版本 37 以上為目標的平台應用程式會獲派 platform_app 網域,而指定 SDK 版本 36 以下為目標的應用程式則會使用 platform_app_36。為保持回溯相容性,platform_app_36 網域仍可開啟 /dev/ashmem。如要簡化兩個網域的政策管理作業,請使用 platform_app_all 屬性。
假設平台應用程式 sample-plat-app 需要從 /dev/foo_device 讀取資料,並將資料寫入 /dev/foo_device。現有的供應商 SELinux 政策可能如下所示:
# This will only allow sample-plat-app to access the device if it # is placed in the platform_app domain (i.e. target SDK version is 37 or higher). allow platform_app foo_device:chr_file rw_file_perms;
不過,如果 sample-plat-app 固定在目標 SDK 版本 36,則會放置在 platform_app_36 網域中,且不會套用先前的 SELinux 政策,並觀察到下列 AVC 拒絕:
auditd : type=1400 audit(0.0:11): avc: denied { read write } for comm="sample-plat-app" path="/dev/foo_device" dev="tmpfs" ino=1609 scontext=u:r:platform_app_36:s0:c512,c768 tcontext=u:object_r:foo_device:s0 tclass=chr_file permissive=0
如要修正這個問題,可以更新政策,因為應用程式應一律有權存取裝置節點:
# This allows sample-plat-app to access the device independent of # target SDK version. allow platform_app_all foo_device:chr_file rw_file_perms;
在某些情況下,platform_app_all可能無法運作。舉例來說,如果將 hal_client_domain() 巨集與 platform_app_all 搭配使用,政策就會無法編譯。這是因為 platform_app_all 是屬性,而 hal_client_domain() 會嘗試將另一個屬性附加至該屬性,但這是不可行的:
# platform_app.te
hal_client_domain(platform_app, hal_foo)
在這些情況下,必須直接使用 platform_app_36 型別,因此您的政策會包含下列內容:
# platform_app.te hal_client_domain(platform_app, hal_foo) # platform_app_36.te hal_client_domain(platform_app_36, hal_foo)
更新 priv_app SELinux 網域
priv_app 網域會根據應用程式的 targetSdkVersion 分割。指定 SDK 版本 37 以上的具備特殊權限應用程式會指派 priv_app 網域,指定 SDK 版本 36 以下的應用程式則會使用 priv_app_36。為保持回溯相容性,priv_app_36 網域仍可開啟 /dev/ashmem。如要簡化兩個網域的政策管理作業,請使用 priv_app_all 屬性。
假設平台應用程式 sample-priv-app 需要從 /dev/foo_device 讀取資料,並將資料寫入 /dev/foo_device。現有的供應商 SELinux 政策可能如下所示:
# This will only allow sample-priv-app to access the device if it # is placed in the priv_app domain (i.e. target SDK version is 37 or higher). allow priv_app foo_device:chr_file rw_file_perms;
不過,如果 sample-priv-app 固定在目標 SDK 版本 36,則會放置在 priv_app_36 網域中,且不會套用先前的 SELinux 政策,並觀察到下列 AVC 拒絕:
auditd : type=1400 audit(0.0:11): avc: denied { read write } for comm="sample-priv-app" path="/dev/foo_device" dev="tmpfs" ino=1609 scontext=u:r:priv_app_36:s0:c512,c768 tcontext=u:object_r:foo_device:s0 tclass=chr_file permissive=0
如要修正這個問題,可以更新政策,因為應用程式應一律有權存取裝置節點:
# This allows sample-priv-app to access the device independent of # target SDK version. allow priv_app_all foo_device:chr_file rw_file_perms;
在某些情況下,priv_app_all可能無法運作。舉例來說,如果將 hal_client_domain() 巨集與 priv_app_all 搭配使用,政策就會無法編譯。這是因為 priv_app_all 是屬性,而 hal_client_domain() 會嘗試將另一個屬性附加至該屬性,但這是不可行的:
# priv_app.te
hal_client_domain(priv_app, hal_foo)
在這些情況下,必須直接使用 priv_app_36 型別,因此政策檔案看起來會像這樣:
# priv_app.te hal_client_domain(priv_app, hal_foo) # priv_app_36.te hal_client_domain(priv_app_36, hal_foo)
更新 untrusted_app SELinux 網域
untrusted_app 網域會根據應用程式的 targetSdkVersion 分割。指定 SDK 37 以上版本的不受信任應用程式會獲派 untrusted_app 網域,指定 SDK 34 至 36 版本 (含) 的應用程式則會獲派新的 untrusted_app_34 網域。untrusted_app_34 網域和 untrusted_app_X 網域 (其中 `X` 是舊版目標 SDK 版本) 仍可開啟 `/dev/ashmem`,以確保回溯相容性。