Update SELinux policy for Pre-reboot Dexopt.

- Add pm.dexopt.* properties.
- Add rules for running artd in chroot.

Bug: 311377497
Test: manual - Run Pre-reboot Dexopt and see no denial.
Change-Id: If5ff9b23e99be033f19ab257c90e0f52bf250ccf
diff --git a/contexts/plat_file_contexts_test b/contexts/plat_file_contexts_test
index 8d6280e..c76f030 100644
--- a/contexts/plat_file_contexts_test
+++ b/contexts/plat_file_contexts_test
@@ -1296,3 +1296,5 @@
 /data/misc/uprobestats-configs/test                               uprobestats_configs_data_file
 
 /tmp                                                              shell_data_file
+
+/mnt/pre_reboot_dexopt                                            pre_reboot_dexopt_file
diff --git a/private/apexd.te b/private/apexd.te
index eeacf65..079489c 100644
--- a/private/apexd.te
+++ b/private/apexd.te
@@ -189,8 +189,10 @@
 # A note on otapreopt_chroot. It used to mount APEXes during postainstall stage of A/B OTAs,
 # but starting from S it just calls into apexd to prepare /apex for otapreoprt. Once the sepolicies
 # around otapreopt_chroot are cleaned up we should be able to remove it from the lists below.
+# dexopt_chroot_setup calls apexd to prepare /apex for Pre-reboot Dexopt, but it
+# needs to mount a tmpfs on /apex for apexd to work on.
 neverallow { domain -apexd -init -otapreopt_chroot } apex_mnt_dir:filesystem { mount unmount };
-neverallow { domain -apexd -init -otapreopt_chroot } apex_mnt_dir:dir { mounton };
+neverallow { domain -apexd -dexopt_chroot_setup -init -otapreopt_chroot } apex_mnt_dir:dir mounton;
 
 # Allow for use in postinstall
 allow apexd otapreopt_chroot:fd use;
@@ -199,6 +201,9 @@
 allow apexd postinstall_apex_mnt_dir:lnk_file create;
 allow apexd proc_filesystems:file r_file_perms;
 
+# Allow for use in Pre-reboot Dexopt.
+allow apexd dexopt_chroot_setup:fd use;
+
 # Allow calling derive_classpath to gather BCP information for staged sessions
 domain_auto_trans(apexd, derive_classpath_exec, apexd_derive_classpath);
 
diff --git a/private/art_exec.te b/private/art_exec.te
new file mode 100644
index 0000000..7f944f6
--- /dev/null
+++ b/private/art_exec.te
@@ -0,0 +1,22 @@
+# A wrapper program that configures the process and executes a command.
+type art_exec, domain, coredomain;
+type art_exec_exec, system_file_type, exec_type, file_type;
+
+# Usually, this program is executed in the caller's domain. For example, it is
+# executed in the `artd` domain when artd calls it. Domain transition will take
+# place as soon as it executes other programs.
+# The only exception is when called by init. In this case, it's executed in its
+# own domain because init should never execute a program without changing to
+# another domain.
+init_daemon_domain(art_exec)
+
+# init calls this program to execute artd in a chroot environment for Pre-reboot
+# Dexopt.
+domain_auto_trans(art_exec, artd_exec, artd)
+
+# "sys_chroot" is for chroot'ing into the chroot environment, created by
+# dexopt_chroot_setup.
+allow art_exec self:global_capability_class_set sys_chroot;
+
+# Allow finding artd in the chroot dir for Pre-reboot Dexopt.
+allow art_exec pre_reboot_dexopt_file:dir search;
diff --git a/private/artd.te b/private/artd.te
index f8e79fb..f74a472 100644
--- a/private/artd.te
+++ b/private/artd.te
@@ -121,10 +121,9 @@
 # /mnt/expand/<volume-uuid>, for cleaning up obsolete managed files.
 allow artd system_data_file:dir r_dir_perms;
 
-# Never allow running other binaries without a domain transition.
-# The only exception is art_exec. It is allowed to use the artd domain because
-# it is a thin wrapper that executes other binaries on behalf of artd.
-neverallow artd ~{art_exec_exec}:file execute_no_trans;
+# Allow art_exec_exec to use the artd domain because it is a thin wrapper that
+# executes other binaries on behalf of artd. Domain transition will take place
+# as soon as art_exec_exec executes other binaries.
 allow artd art_exec_exec:file rx_file_perms;
 
 # Allow running other binaries in their own domains.
@@ -143,3 +142,43 @@
 # artd needs to reopen a memfd with readonly in order to pass it to subprocesses
 # that don't have write permissions on memfds.
 allow artd artd_tmpfs:file open;
+
+# For Pre-reboot Dexopt.
+
+# Allow init to execute artd through art_exec.
+allow artd art_exec:fd use;
+
+# During Pre-reboot Dexopt, artd needs one more capability:
+# - "sys_admin" is for bind-mounting temp dirs at
+#   /data/misc/apexdata/com.android.art and /data/misc/odrefresh, to run
+#   odrefresh innocuously (in a way that doesn't affect the real boot images,
+#   metrics, etc.).
+allow artd self:global_capability_class_set sys_admin;
+
+# Allow running other binaries in their own domains.
+domain_auto_trans(artd, derive_classpath_exec, derive_classpath)
+domain_auto_trans(artd, odrefresh_exec, odrefresh)
+
+# Allow accessing Pre-reboot Dexopt files.
+allow artd pre_reboot_dexopt_file:dir { getattr search };
+
+# Allow reading /init.environ.rc in chroot, to extract env vars from it.
+allow artd rootfs:file { read open getattr };
+
+# Allow managing Pre-reboot Dexopt temp files.
+# The root of the temp dir that artd uses during Pre-reboot Dexopt is labeled
+# pre_reboot_dexopt_artd_file. Inside the temp dir, we create files and dirs and
+# relabel them after creation, so we need relabelfrom.
+allow artd pre_reboot_dexopt_artd_file:dir { create_dir_perms relabelfrom };
+allow artd pre_reboot_dexopt_artd_file:file { create_file_perms relabelfrom };
+
+# Allow bind-mounting at /data/misc/apexdata/com.android.art and
+# /data/misc/odrefresh and restorecon, to run odrefresh innocuously.
+allow artd { apex_art_data_file odrefresh_data_file }:dir relabelto;
+allow artd { apex_art_data_file odrefresh_data_file pre_reboot_dexopt_artd_file }:dir mounton;
+
+# Neverallow rules.
+
+# Never allow running other binaries without a domain transition.
+# The exception for art_exec_exec is explained above.
+neverallow artd ~{art_exec_exec}:file execute_no_trans;
diff --git a/private/derive_classpath.te b/private/derive_classpath.te
index 4f15d5a..8dd6572 100644
--- a/private/derive_classpath.te
+++ b/private/derive_classpath.te
@@ -24,3 +24,9 @@
 allow derive_classpath postinstall_dexopt:file read;
 allow derive_classpath postinstall_dexopt:lnk_file read;
 allow derive_classpath postinstall_dexopt_tmpfs:file rw_file_perms;
+
+# Allow to be called by artd in Pre-reboot Dexopt.
+allow derive_classpath artd:fd use;
+
+# Allow writing to Pre-reboot Dexopt temp files.
+allow derive_classpath pre_reboot_dexopt_artd_file:file { open read write };
diff --git a/private/dexopt_chroot_setup.te b/private/dexopt_chroot_setup.te
index f7bd17a..c34a30b 100644
--- a/private/dexopt_chroot_setup.te
+++ b/private/dexopt_chroot_setup.te
@@ -1,3 +1,4 @@
+# A service that sets up the chroot environment for Pre-reboot Dexopt.
 type dexopt_chroot_setup, domain, coredomain;
 type dexopt_chroot_setup_exec, system_file_type, exec_type, file_type;
 type dexopt_chroot_setup_tmpfs, file_type;
@@ -10,10 +11,10 @@
 
 init_daemon_domain(dexopt_chroot_setup)
 
-# Use tmpfs_domain() which will give tmpfs files created by dexopt_chroot_setup their
-# own label, which differs from other labels created by other processes.
-# This allows to distinguish in policy files created by dexopt_chroot_setup vs other
-# processes.
+# Use tmpfs_domain() which will give tmpfs files created by dexopt_chroot_setup
+# their own label, which differs from other labels created by other processes.
+# This allows to distinguish in policy files created by dexopt_chroot_setup vs
+# other processes.
 tmpfs_domain(dexopt_chroot_setup)
 
 # libart (mark_compact.cc) has some intialization code that touches the cache
@@ -21,3 +22,118 @@
 allow dexopt_chroot_setup apex_module_data_file:dir { getattr search };
 r_dir_file(dexopt_chroot_setup, apex_art_data_file)
 userfaultfd_use(dexopt_chroot_setup)
+
+# Allow getting root capabilities to bypass permission checks.
+# - "sys_admin" is for performing mount and umount.
+# - "sys_chroot" is for performing chroot.
+allow dexopt_chroot_setup self:global_capability_class_set { sys_admin sys_chroot };
+
+# Allow managing its own files.
+# The root of the temp dir that dexopt_chroot_setup uses is labeled
+# pre_reboot_dexopt_file.
+allow dexopt_chroot_setup pre_reboot_dexopt_file:dir create_dir_perms;
+allow dexopt_chroot_setup pre_reboot_dexopt_file:file create_file_perms;
+
+# Allow accessing /proc/filesystems.
+allow dexopt_chroot_setup proc_filesystems:file r_file_perms;
+
+# Allow accessing block devices (/dev/block/...).
+allow dexopt_chroot_setup block_device:dir { getattr search };
+
+# Allow mounting file systems, to create a chroot environment.
+allow dexopt_chroot_setup {
+  apex_mnt_dir
+  binderfs
+  cgroup
+  cgroup_v2
+  debugfs_tracing_debug
+  device
+  devpts
+  fs_bpf
+  fusectlfs
+  linkerconfig_file
+  metadata_file
+  mnt_expand_file
+  pre_reboot_dexopt_file
+  proc
+  pstorefs
+  rootfs
+  selinuxfs
+  sysfs
+  system_data_file
+  system_data_root_file
+  system_file
+  tmpfs
+  vendor_file
+}:dir mounton;
+
+allow dexopt_chroot_setup { tmpfs labeledfs }:filesystem mount;
+
+allow dexopt_chroot_setup {
+  binderfs
+  cgroup
+  cgroup_v2
+  debugfs_tracing_debug
+  devpts
+  fs_bpf
+  fusectlfs
+  labeledfs
+  proc
+  pstorefs
+  selinuxfs
+  sysfs
+  tmpfs
+}:filesystem unmount;
+
+# Allow reading /apex in chroot.
+r_dir_file(dexopt_chroot_setup, apex_mnt_dir)
+allow dexopt_chroot_setup apex_info_file:file r_file_perms;
+
+# Allow writing an empty linker config in chroot to suppress linker warnings.
+# The empty linker config is used until linkerconfig has run.
+# In chroot, we're reusing the type outside the chroot, to reuse all the rules
+# for it for other domains, even though we're not changing the real linker
+# config outside the chroot.
+allow dexopt_chroot_setup linkerconfig_file:dir { write add_name };
+allow dexopt_chroot_setup linkerconfig_file:file { create write };
+
+# Allow using the `rootcontext=` option when mounting tmpfs, so we can give the
+# right labels to /apex, /linkerconfig, /mnt/artd_tmp in chroot.
+# Combined with `allow file_type tmpfs:filesystem associate;`, this allows
+# giving any labels to any tmpfs filesystems as soon as they are mounted.
+# Note that those tmpfs filesystems are known to be empty at the time where the
+# labels are given, and this rule doesn't allow relabeling any existing tmpfs.
+allow dexopt_chroot_setup tmpfs:filesystem relabelfrom;
+
+# Allow executing art_exec_exec without a domain transition because it is a thin
+# wrapper that executes other binaries on behalf of dexopt_chroot_setup. Domain
+# transition will take place as soon as art_exec_exec executes other binaries.
+allow dexopt_chroot_setup art_exec_exec:file rx_file_perms;
+
+# Allow running other binaries in their own domains.
+domain_auto_trans(dexopt_chroot_setup, apexd_exec, apexd)
+domain_auto_trans(dexopt_chroot_setup, linkerconfig_exec, linkerconfig)
+
+# Allow running snapshotctl through init, to map and unmap block devices.
+set_prop(dexopt_chroot_setup, snapshotctl_prop)
+
+# Neverallow rules.
+
+# Never allow running other binaries without a domain transition.
+# The exception for art_exec_exec is explained above.
+neverallow dexopt_chroot_setup ~{art_exec_exec}:file execute_no_trans;
+
+# Given how powerful this domain is, it shouldn't be used for other purposes.
+neverallow { domain -init } dexopt_chroot_setup:process transition;
+neverallow * dexopt_chroot_setup:process dyntransition;
+
+# Never allow other processes to access the temp dirs for Pre-reboot Dexopt.
+neverallow {
+  domain
+  -art_exec
+  -artd
+  -dexopt_chroot_setup
+  -init
+  -system_server
+  -vendor_init
+} pre_reboot_dexopt_file:dir *;
diff --git a/private/domain.te b/private/domain.te
index aa0a5bb..fd1d9fd 100644
--- a/private/domain.te
+++ b/private/domain.te
@@ -190,6 +190,7 @@
   -webview_zygote
   -system_server
   -artd
+  -dexopt_chroot_setup
   -audioserver
   -cameraserver
   -mediadrmserver
@@ -526,6 +527,7 @@
 neverallow {
     domain
     -apexd
+    -dexopt_chroot_setup
     recovery_only(`-fastbootd')
     -init
     -kernel
@@ -676,7 +678,7 @@
 
 # Only init and otapreopt_chroot should be mounting filesystems on locations
 # labeled system or vendor (/product and /vendor respectively).
-neverallow { domain -init -otapreopt_chroot } { system_file_type vendor_file_type }:dir_file_class_set mounton;
+neverallow { domain -dexopt_chroot_setup -init -otapreopt_chroot } { system_file_type vendor_file_type }:dir_file_class_set mounton;
 
 # Only allow init and vendor_init to read/write mm_events properties
 # NOTE: dumpstate is allowed to read any system property
@@ -829,3 +831,5 @@
 neverallow { domain -init -aconfigd -system_server } aconfig_storage_metadata_file:dir no_w_dir_perms;
 neverallow { domain -init -aconfigd -system_server } aconfig_storage_metadata_file:file no_w_file_perms;
 
+neverallow { domain -dexopt_chroot_setup -init } proc:{ file dir } mounton;
+neverallow { domain -dexopt_chroot_setup -init -zygote } proc_type:{ file dir } mounton;
diff --git a/private/file.te b/private/file.te
index 50ea4c3..b5b22a2 100644
--- a/private/file.te
+++ b/private/file.te
@@ -132,11 +132,6 @@
 # /apex/com.android.compos/bin/compos_key_helper
 type compos_key_helper_exec, exec_type, file_type, system_file_type;
 
-# /apex/com.android.art/bin/art_exec
-# This executable does not have its own domain because it is executed in the caller's domain. For
-# example, it is executed in the `artd` domain when artd calls it.
-type art_exec_exec, system_file_type, exec_type, file_type;
-
 # Filesystem entry for for PRNG seeder socket.  Processes require
 # write permission on this to connect, and needs to be mlstrustedobject
 # in to satisfy MLS constraints for trusted domains.
@@ -163,3 +158,10 @@
 
 # /data/misc/connectivityblobdb
 type connectivityblob_data_file, file_type, data_file_type, core_data_file_type;
+
+# Type for /mnt/pre_reboot_dexopt
+type pre_reboot_dexopt_file, file_type;
+
+# Type for /mnt/artd_tmp in the Pre-reboot Dexopt chroot
+# This type is set on the directory through the `rootcontext=` mount option.
+type pre_reboot_dexopt_artd_file, file_type;
diff --git a/private/file_contexts b/private/file_contexts
index 81391a8..193c033 100644
--- a/private/file_contexts
+++ b/private/file_contexts
@@ -902,3 +902,12 @@
 /(system|product)/bin/check_dynamic_partitions  u:object_r:postinstall_exec:s0
 /(system|product)/bin/otapreopt_script          u:object_r:postinstall_exec:s0
 /(system|product)/bin/otapreopt                 u:object_r:postinstall_dexopt_exec:s0
+
+#############################
+# For Pre-reboot Dexopt (see art/dexopt_chroot_setup/README.md)
+
+# Managed by dexopt_chroot_setup.
+# Note that pre_reboot_dexopt_file also applies to any tmpfs mounted by
+# dexopt_chroot_setup inside chroot, in addition to the files and directories
+# matching the pattern below.
+/mnt/pre_reboot_dexopt(/.*)?  u:object_r:pre_reboot_dexopt_file:s0
diff --git a/private/linkerconfig.te b/private/linkerconfig.te
index bd46ca4..ce26fd2 100644
--- a/private/linkerconfig.te
+++ b/private/linkerconfig.te
@@ -27,4 +27,13 @@
 allow linkerconfig postinstall_apex_mnt_dir:dir r_dir_perms;
 allow linkerconfig postinstall_apex_mnt_dir:file r_file_perms;
 
-neverallow { domain -init -linkerconfig -otapreopt_chroot } linkerconfig_exec:file no_x_file_perms;
+# Allow for use in Pre-reboot Dexopt.
+allow linkerconfig dexopt_chroot_setup:fd use;
+
+neverallow {
+  domain
+  -dexopt_chroot_setup
+  -init
+  -linkerconfig
+  -otapreopt_chroot
+} linkerconfig_exec:file no_x_file_perms;
diff --git a/private/odrefresh.te b/private/odrefresh.te
index cb8a535..899b0d9 100644
--- a/private/odrefresh.te
+++ b/private/odrefresh.te
@@ -51,17 +51,28 @@
 dontaudit odrefresh devpts:chr_file rw_file_perms;
 dontaudit odrefresh adbd:unix_stream_socket { getattr read write };
 
-# No other processes should be creating files in the staging area.
-neverallow { domain -init -odrefresh -compos_fd_server } apex_art_staging_data_file:file open;
-
-# No processes other than init, odrefresh and system_server access
-# odrefresh_data_files.
-neverallow { domain -init -odrefresh -system_server } odrefresh_data_file:dir *;
-neverallow { domain -init -odrefresh -system_server } odrefresh_data_file:file *;
-
 # Read access to SELinux context files, for restorecon.
 allow odrefresh file_contexts_file:file r_file_perms;
 allow odrefresh seapp_contexts_file:file r_file_perms;
 
 # Check validity of SELinux context, for restorecon.
 selinux_check_context(odrefresh)
+
+# odrefresh iterates over all properties to find the ones that it's interested
+# in. It's okay to ignore denials on other properties.
+dontaudit odrefresh property_type:file r_file_perms;
+
+# Allow to be called by artd in Pre-reboot Dexopt.
+allow odrefresh artd:fd use;
+
+# Neverallow rules.
+
+# No other processes should be creating files in the staging area.
+neverallow { domain -init -odrefresh -compos_fd_server } apex_art_staging_data_file:file open;
+
+# No processes other than init, odrefresh, system_server, and artd access
+# odrefresh_data_files.
+# Among those, artd only needs to bind-mount /data/misc/odrefresh, but doesn't
+# need to access the files.
+neverallow { domain -init -odrefresh -system_server -artd } odrefresh_data_file:dir *;
+neverallow { domain -init -odrefresh -system_server } odrefresh_data_file:file *;
diff --git a/private/property_contexts b/private/property_contexts
index b4458ee..163f75e 100644
--- a/private/property_contexts
+++ b/private/property_contexts
@@ -675,9 +675,11 @@
 pm.dexopt.boot-after-ota.concurrency                    u:object_r:exported_pm_prop:s0 exact int
 pm.dexopt.boot-after-mainline-update                    u:object_r:exported_pm_prop:s0 exact string
 pm.dexopt.boot-after-mainline-update.concurrency        u:object_r:exported_pm_prop:s0 exact int
-
 pm.dexopt.post-boot                                     u:object_r:exported_pm_prop:s0 exact string
+pm.dexopt.post-boot.concurrency                         u:object_r:exported_pm_prop:s0 exact int
 pm.dexopt.ab-ota                                        u:object_r:exported_pm_prop:s0 exact string
+pm.dexopt.ab-ota.concurrency                            u:object_r:exported_pm_prop:s0 exact int
+
 pm.dexopt.cmdline                                       u:object_r:exported_pm_prop:s0 exact string
 pm.dexopt.inactive                                      u:object_r:exported_pm_prop:s0 exact string
 pm.dexopt.install                                       u:object_r:exported_pm_prop:s0 exact string
diff --git a/private/system_server.te b/private/system_server.te
index a244ff4..20511cb 100644
--- a/private/system_server.te
+++ b/private/system_server.te
@@ -1618,3 +1618,7 @@
 # Do not allow any domain other than init or system server to get or set the property
 neverallow { domain -init -system_server } crashrecovery_prop:property_service set;
 neverallow { domain -init -dumpstate -system_server } crashrecovery_prop:file no_rw_file_perms;
+
+# Allow accessing /mnt/pre_reboot_dexopt/chroot, to load the new service-art.jar
+# in Pre-reboot Dexopt.
+allow system_server pre_reboot_dexopt_file:dir { getattr search };
diff --git a/public/domain.te b/public/domain.te
index a8fdc9b..db3261e 100644
--- a/public/domain.te
+++ b/public/domain.te
@@ -1069,9 +1069,6 @@
 # and modification of executable pages are unsafe.
 neverallow { domain -untrusted_app_25 -untrusted_app_27 } file_type:file execmod;
 
-neverallow { domain -init } proc:{ file dir } mounton;
-neverallow { domain -init -zygote } proc_type:{ file dir } mounton;
-
 # Ensure that all types assigned to processes are included
 # in the domain attribute, so that all allow and neverallow rules
 # written on domain are applied to all processes.