政策相容性

本頁說明 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_contextsservice_contexts 等為 /system 元件提供標籤。如果供應商政策中新增了 /system 元件的標籤,可能無法進行僅限架構的 OTA 更新。

供應商 (/vendor)

AOSP SELinux 政策已標記平台互動的 vendor 分區部分,因此可為平台程序編寫 SELinux 規則,以便與 vendor 分區部分通訊或存取這些部分。範例:

/vendor path 平台提供的標籤 平台程序 (視標籤而定)
/vendor(/.*)? vendor_file 架構中的所有 HAL 用戶端,ueventd 等。
/vendor/framework(/.*)? vendor_framework_file dex2oatappdomain
/vendor/app(/.*)? vendor_app_file dex2oatinstalldidmap 等。
/vendor/overlay(/.*) vendor_overlay_file system_serverzygoteidmap 等。

因此,在 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 中,平台供應商政策都使用 genfsconprocfs 中的檔案加上標籤。

建議:僅限平台政策標籤 /proc。 如果供應商程序需要存取 /proc 中目前標示預設標籤 (proc) 的檔案,供應商政策不應明確標示這些檔案,而應使用一般 proc 類型,為供應商網域新增規則。這樣一來,平台更新就能配合日後透過 procfs 公開的 Kernel 介面,並視需要明確標示。

Debugfs (/sys/kernel/debug)

Debugfs 可在 file_contextsgenfscon 中加上標籤。在 Android 7.0 至 Android 10 中,平台和供應商標籤都會顯示。debugfs

在 Android 11 中,debugfs 無法在正式版裝置上存取或掛接。裝置製造商應移除 debugfs

Tracefs (/sys/kernel/debug/tracing)

Tracefs 可在 file_contextsgenfscon 中加上標籤。在 Android 7.0 中,只有平台標籤 tracefs

建議:只有平台可以標示 tracefs

Sysfs (/sys)

/sys 中的檔案可能會同時使用 file_contextsgenfscon 加上標籤。在 Android 7.0 中,平台和供應商都會使用 genfsconsysfs 中的檔案加上標籤。

建議:平台可能會標示非裝置專用的 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_contextsseapp_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 標籤版本。

平台公共政策

平台 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_initdebugfs 都是型別:

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_202504sysfs_202504 分別對應至 vendor_initsysfs 類型,這些是平台定義的類型。
  • 建構系統會產生識別資訊對應檔 /system/etc/selinux/mapping/202504.cil。由於 systemvendor 分區都使用相同的 202504 版本,因此對應檔案包含從 type_202504type 的身分對應。舉例來說,vendor_init_202504 對應至 vendor_initsysfs_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_initsysfs_usb 類型的存取權。

為維持與舊版 vendor 分區的相容性,每當新增公開型別時,該型別必須對應至對應檔案 system/sepolicy/private/compat/ver/ver.cil 中的至少一個版本化屬性,或列於 system/sepolicy/private/compat/ver/ver.ignore.cil 下方,說明先前供應商版本中沒有相符的型別。

平台政策、供應商政策和對應檔案的組合,可讓系統在不更新供應商政策的情況下進行更新。此外,系統會自動將屬性轉換為版本化屬性,因此供應商政策不必處理版本化作業,可繼續使用公開型別。

system_ext 公開和產品公開政策

從 Android 11 開始,system_extproduct 分區可將指定公開型別匯出至 vendor 分區。與平台公開政策相同,供應商政策會使用自動轉換為版本化屬性的類型和規則,例如從 type 轉換為 type_ver,其中 vervendor 分區的供應商 API 級別。

如果 system_extproduct 分區是根據相同平台版本 ver,建構系統會產生基本對應檔案至 system_ext/etc/selinux/mapping/ver.cilproduct/etc/selinux/mapping/ver.cil,其中包含從 typetype_ver 的身分對應。供應商政策可透過版本化屬性 type_ver 存取 type

如果只有 system_extproduct 分區更新 (例如從 ver 更新至 ver+1 或更新版本),而 vendor 分區仍為 ver,供應商政策可能會失去 system_extproduct 分區的存取權。為避免中斷,system_extproduct 分區應提供從具體型別到 type_ver 屬性的對應檔案。如果合作夥伴支援 ver vendor 分區 (使用 ver+1 或更新版本 system_extproduct 分區),則須負責維護對應檔案。

如要將對應檔案安裝至 system_extproduct 分區,裝置實作人員或供應商應採取下列行動:

  1. 將產生的基礎對應檔案從 ver system_extproduct 分區 複製到來源樹狀結構
  2. 視需要修改對應檔案。
  3. 將對應檔案安裝至 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_extfoo_typebar_type

Android 9 的屬性變更

升級至 Android 9 的裝置可以使用下列屬性,但搭載 Android 9 的裝置不得使用。

違規者屬性

Android 9 包含下列網域相關屬性:

  • data_between_core_and_vendor_violators. 所有網域的屬性,這些網域違反不得在 vendorcoredomains 之間透過路徑共用檔案的規定。平台和供應商程序不應使用磁碟上的檔案進行通訊 (ABI 不穩定)。建議:
    • 供應商代碼應使用 /data/vendor
    • 系統不應使用 /data/vendor
  • system_executes_vendor_violators。所有系統網域 (initshell domains 除外) 的屬性,都不得違反不得執行供應商二進位檔的規定。執行供應商二進位檔時,API 不穩定。平台不應直接執行供應商二進位檔。建議:
    • 這類平台對供應商二進位檔的依附元件必須位於 HIDL HAL 後方。

    • coredomains,需要存取供應商二進位檔的 coredomains 應移至 vendor 分區,並停止成為 coredomain

不受信任的屬性

代管任意程式碼的不受信任應用程式不應存取 HwBinder 服務,但可存取被視為足夠安全,可供這類應用程式存取的服務 (請參閱下方的安全服務)。主要原因有二:

  1. HwBinder 伺服器不會執行用戶端驗證,因為 HIDL 目前不會公開呼叫端 UID 資訊。即使 HIDL 確實公開這類資料,許多 HwBinder 服務不是在應用程式層級以下運作 (例如 HAL),就是不得依據應用程式 ID 進行授權。因此,為求安全,預設假設是每個 HwBinder 服務都會將所有用戶端視為同樣有權執行服務提供的作業。
  2. HAL 伺服器 (HwBinder 服務的子集) 包含的程式碼,安全問題發生率高於 system/core 元件,且可存取堆疊的較低層 (一路到硬體),因此增加規避 Android 安全性模型的機會。

安全服務

安全服務包括:

  • same_process_hwservice。這些服務 (根據定義) 會在用戶端程序中執行,因此與程序執行的用戶端網域具有相同的存取權。
  • coredomain_hwservice。這些服務不會造成與原因 #2 相關的風險。
  • hal_configstore_ISurfaceFlingerConfigs。這項服務專為任何網域設計。
  • hal_graphics_allocator_hwservice。這些作業也由 surfaceflinger Binder 服務提供,應用程式可存取該服務。
  • hal_omx_hwservice。這是 mediacodec Binder 服務的 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 對話。

  • 需要直接存取 vendor HAL 的應用程式應有自己的供應商定義 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
    • 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 一併載入。
  • 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,適用於 servicemanagerservice_context 沒有裝置專屬標籤。
    • 必須位於 system 分區的 /system/etc/selinux/plat_service_contexts,並在啟動時由 servicemanager 與供應商 service_contexts 一併載入。
  • 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。這是因為 vendorsystem 程序之間的所有互動都必須經過 hwservicemanager/hwbinder
  • plat_hwservice_contexts
    • Android 平台 hwservice_context,沒有裝置專屬標籤。hwservicemanager
    • 必須位於 system 分區的 /system/etc/selinux/plat_hwservice_contexts,並在啟動時與 vendor_hwservice_contexts 一併由 hwservicemanager 載入。
  • 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.mkBOARD_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.
  • 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/.
  • 非平台 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 存取程序 foomemfds,如下所示:

# 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 … logavc 拒絕有關,且時間戳記為 0.0.86。這份記錄會解碼 avc 拒絕中的路徑值 (十六進位字串),並提供 memfd 記憶體區域的名稱,有助於瞭解共用緩衝區。來源內容和目標內容有助於瞭解哪些程序需要共用記憶體。從上述範例可清楚看出,mediaserver 程序必須能夠存取 mediaextractormemfds。因此,適用的政策為:

# 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_apppriv_appuntrusted_app 安全性網域中開啟 /dev/ashmem,以及在 memfd 上叫用 ashmem ioctl 指令。方法是根據目標 SDK 版本分割這些應用程式網域。這項變更會導入 platform_app_36priv_app_36untrusted_app_34 安全性網域,這些網域與其他應用程式網域會保留 ashmem 開啟權限,並能在 memfds 上叫用 ashmem ioctl 指令。

在日後的 Android 版本中,保留在 memfds 上開啟 ashmem 裝置及叫用 ashmem ioctl 指令權限的應用程式集,將縮減為僅限 platform_app_36priv_app_36untrusted_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`,以確保回溯相容性。