diff --git a/private/domain.te b/private/domain.te index 326e62ae1ca1fd6547ca76a04217d751805d929d..dda8f21e9413cc0b052c91f0e20df7c3bfba6213 100644 --- a/private/domain.te +++ b/private/domain.te @@ -10,7 +10,8 @@ allow domain crash_dump:process sigchld; # heap profiling, as initialization will fail if it does not have the # necessary SELinux permissions. get_prop(domain, heapprofd_prop); -userdebug_or_eng(`can_profile_heap({ +# Allow heap profiling on debug builds. +userdebug_or_eng(`can_profile_heap_userdebug_or_eng({ domain -bpfloader -init diff --git a/private/ephemeral_app.te b/private/ephemeral_app.te index 3500c0f7f3f363c6a4cbadecac1cb79f521f5d9f..9a6a300a0cd138adbc0e8288ddb2820a88c34249 100644 --- a/private/ephemeral_app.te +++ b/private/ephemeral_app.te @@ -49,6 +49,10 @@ allow ephemeral_app traced:fd use; allow ephemeral_app traced_tmpfs:file { read write getattr map }; unix_socket_connect(ephemeral_app, traced_producer, traced) +# Allow heap profiling if the app opts in by being marked +# profileable/debuggable. +can_profile_heap(ephemeral_app) + # allow ephemeral apps to use UDP sockets provided by the system server but not # modify them other than to connect allow ephemeral_app system_server:udp_socket { diff --git a/private/heapprofd.te b/private/heapprofd.te index 5a179907dace93219d412830ad548a0d8968f3dd..7f8d8d62b89afa2d2fd94d3981b2dfe4ca58db02 100644 --- a/private/heapprofd.te +++ b/private/heapprofd.te @@ -1,33 +1,47 @@ -# Android Heap Profiler Daemon go/heapprofd +# Android heap profiling daemon. go/heapprofd. +# +# On user builds, this daemon is responsible for receiving the initial +# profiling configuration, finding matching target processes (if profiling by +# process name), and sending the activation signal to them (+ setting system +# properties for new processes to start profiling from startup). When profiling +# is triggered in a process, it spawns a private heapprofd subprocess (in its +# own SELinux domain), which will exclusively handle profiling of its parent. +# +# On debug builds, this central daemon performs profiling for all target +# processes (which talk directly to this daemon). type heapprofd_exec, exec_type, file_type, system_file_type; init_daemon_domain(heapprofd) set_prop(heapprofd, heapprofd_prop); +# Necessary for /proc/[pid]/cmdline access & sending signals. +typeattribute heapprofd mlstrustedsubject; + +# Allow sending signals to processes. This excludes SIGKILL, SIGSTOP and +# SIGCHLD, which are controlled by separate permissions. +allow heapprofd self:capability kill; + +# When scanning /proc/[pid]/cmdline to find matching processes for by-name +# profiling, only whitelisted domains will be allowed by SELinux. Avoid +# spamming logs with denials for entries that we can not access. +dontaudit heapprofd domain:dir { search open }; + +# Write trace data to the Perfetto traced daemon. This requires connecting to +# its producer socket and obtaining a (per-process) tmpfs fd. +allow heapprofd traced:fd use; +allow heapprofd traced_tmpfs:file { read write getattr map }; +unix_socket_connect(heapprofd, traced_producer, traced) + +# When handling profiling for all processes, heapprofd needs to read +# executables/libraries/etc to do stack unwinding. userdebug_or_eng(` - # TODO(fmayer): We will also need this on user to read /proc/<pid>/cmdline - # and send signals. - typeattribute heapprofd mlstrustedsubject; - # Allow to send signal to processes. - # This excludes SIGKILL, SIGSTOP and SIGCHLD, - # which are controlled by separate permissions. - allow heapprofd self:capability kill; - - # Executables and libraries. - # These are needed to read the ELF binary data needed for unwinding. r_dir_file(heapprofd, system_file_type) r_dir_file(heapprofd, apk_data_file) r_dir_file(heapprofd, dalvikcache_data_file) r_dir_file(heapprofd, vendor_file_type) ') -# Write trace data to the Perfetto traced damon. This requires connecting to its -# producer socket and obtaining a (per-process) tmpfs fd. -allow heapprofd traced:fd use; -allow heapprofd traced_tmpfs:file { read write getattr map }; -unix_socket_connect(heapprofd, traced_producer, traced) - never_profile_heap(`{ bpfloader init diff --git a/private/isolated_app.te b/private/isolated_app.te index 3443dc439d4c656c59675c86ca91e24a485ec3ef..017f46b06241c109f85406f92136310323c3d43e 100644 --- a/private/isolated_app.te +++ b/private/isolated_app.te @@ -60,6 +60,10 @@ allow isolated_app traced:fd use; allow isolated_app traced_tmpfs:file { read write getattr map }; unix_socket_connect(isolated_app, traced_producer, traced) +# Allow heap profiling if the main app has been marked as profileable or +# debuggable. +can_profile_heap(isolated_app) + ##### ##### Neverallow ##### diff --git a/private/priv_app.te b/private/priv_app.te index b6828f063258c76b009c3db91e15c426e6dd10d6..9232bd0f947b5cb10c197a40f2c3f0915de9ad53 100644 --- a/private/priv_app.te +++ b/private/priv_app.te @@ -144,6 +144,10 @@ allow priv_app traced:fd use; allow priv_app traced_tmpfs:file { read write getattr map }; unix_socket_connect(priv_app, traced_producer, traced) +# Allow heap profiling if the app opts in by being marked +# profileable/debuggable. +can_profile_heap(priv_app) + # suppress denials for non-API accesses. dontaudit priv_app exec_type:file getattr; dontaudit priv_app device:dir read; diff --git a/private/untrusted_app_all.te b/private/untrusted_app_all.te index ba707516b547408797fd12140fc1d42995dcdca1..a4af4e70825742e201df2f0ef2aa61a49311251c 100644 --- a/private/untrusted_app_all.te +++ b/private/untrusted_app_all.te @@ -123,6 +123,10 @@ allow untrusted_app_all traced:fd use; allow untrusted_app_all traced_tmpfs:file { read write getattr map }; unix_socket_connect(untrusted_app_all, traced_producer, traced) +# Allow heap profiling if the app opts in by being marked +# profileable/debuggable. +can_profile_heap(untrusted_app_all) + # allow untrusted apps to use UDP sockets provided by the system server but not # modify them other than to connect allow untrusted_app_all system_server:udp_socket { diff --git a/public/te_macros b/public/te_macros index 149d5ac22d8b6ffbdb50f1eb93512986ceb9d499..ca6070b616bb31203dc9dee861b87f5c0d113d98 100644 --- a/public/te_macros +++ b/public/te_macros @@ -647,31 +647,66 @@ define(`hal_attribute_hwservice', ` ################################### # can_profile_heap(domain) -# never_profile_heap(domain) -# -# Opt in our out of heap profiling. -# This will allow a heap profiling daemon to read this -# process' address space in order to support unwinding. +# Allow processes within the domain to have their heap profiled by heapprofd. # +# Note that profiling is performed differently between debug and user builds. +# This macro covers both user and debug builds, but see +# can_profile_heap_userdebug_or_eng for a variant that can be used when +# allowing profiling for a domain only on debug builds, without granting +# the exec permission. The exec permission is necessary for user builds, but +# only a nice-to-have for development and testing purposes on debug builds. define(`can_profile_heap', ` + # Allow central daemon to send signal for client initialization. + allow heapprofd $1:process signal; + + # Allow executing a private heapprofd process to handle profiling on + # user builds (also debug builds for testing & development purposes). + allow $1 heapprofd_exec:file rx_file_perms; + + # Allow directory & file read to the central heapprofd daemon, as it scans + # /proc/[pid]/cmdline for by-process-name profiling configs. + # Note that this excludes /proc/[pid]/mem, as it requires ptrace capabilities. + allow heapprofd $1:file r_file_perms; + allow heapprofd $1:dir r_dir_perms; + + # On debug builds, central daemon can handle profiling of all processes + # directly. + userdebug_or_eng(` + # Allow connecting to the daemon. + unix_socket_connect($1, heapprofd, heapprofd) + # Allow daemon to use the passed fds. + allow heapprofd $1:fd use; + ') +') + +################################### +# can_profile_heap_userdebug_or_eng(domain) +# Allow processes within the domain to have their heap profiled by heapprofd on +# debug builds only. +# +# Only necessary when can_profile_heap cannot be applied, see its description +# for rationale. +define(`can_profile_heap_userdebug_or_eng', ` userdebug_or_eng(` - # RT signal for client initialization. + # Allow central daemon to send signal for client initialization. allow heapprofd $1:process signal; - # Connect to heapprofd service. + # Allow connecting to the daemon. unix_socket_connect($1, heapprofd, heapprofd) - # To receive file descriptor. + # Allow daemon to use the passed fds. allow heapprofd $1:fd use; # To read from the received file descriptors. # /proc/[pid]/maps and /proc/[pid]/mem have the same SELinux label as the # process they relate to. allow heapprofd $1:file r_file_perms; - # This is needed to search the /proc/[pid] directory. + # Allow searching the /proc/[pid] directory for cmdline. allow heapprofd $1:dir r_dir_perms; - allow heapprofd $1:process signal; ') ') +################################### +# never_profile_heap(domain) +# Opt out of heap profiling by heapprofd. define(`never_profile_heap', ` neverallow heapprofd $1:file read; neverallow heapprofd $1:process signal;