Skip to content
Snippets Groups Projects
Commit ca0690e8 authored by Ryan Savitski's avatar Ryan Savitski
Browse files

Allow heap profiling of certain app domains on user builds

This patch extends the current debug-specific rules to cover user
builds. As a reminder, on user, the target process fork-execs a private
heapprofd process, which then performs stack unwinding & talking to the
central tracing daemon while staying in the target's domain. The central
heapprofd daemon is only responsible for identifying targets & sending
the activation signal. On the other hand, on debug, the central
heapprofd can handle all processes directly, so the necessary SELinux
capabilities depend on the build type.

These rules are necessary but not sufficient for profiling. For zygote
children, the libc triggering logic will also check for the app to
either be debuggable, or go/profileable.

For more context, see go/heapprofd-security & go/heapprofd-design.

Note that I've had to split this into two separate macros, as
exec_no_trans - which is necessary on user, but nice-to-have on debug -
conflicts with a lot of neverallows (e.g. HALs and system_server) for
the wider whitelisting that we do on debug builds.

Test: built & flashed on {blueline-userdebug, blueline-user}, activated profiling of whitelisted/not domains & checked for lack of denials in logcat.
Bug: 120409382
Change-Id: Id0defc3105b99f777bcee2046d9894a2b39c6a29
parent 1bbda7e6
No related branches found
No related tags found
No related merge requests found
...@@ -10,7 +10,8 @@ allow domain crash_dump:process sigchld; ...@@ -10,7 +10,8 @@ allow domain crash_dump:process sigchld;
# heap profiling, as initialization will fail if it does not have the # heap profiling, as initialization will fail if it does not have the
# necessary SELinux permissions. # necessary SELinux permissions.
get_prop(domain, heapprofd_prop); 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 domain
-bpfloader -bpfloader
-init -init
......
...@@ -49,6 +49,10 @@ allow ephemeral_app traced:fd use; ...@@ -49,6 +49,10 @@ allow ephemeral_app traced:fd use;
allow ephemeral_app traced_tmpfs:file { read write getattr map }; allow ephemeral_app traced_tmpfs:file { read write getattr map };
unix_socket_connect(ephemeral_app, traced_producer, traced) 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 # allow ephemeral apps to use UDP sockets provided by the system server but not
# modify them other than to connect # modify them other than to connect
allow ephemeral_app system_server:udp_socket { allow ephemeral_app system_server:udp_socket {
......
# 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; type heapprofd_exec, exec_type, file_type, system_file_type;
init_daemon_domain(heapprofd) init_daemon_domain(heapprofd)
set_prop(heapprofd, heapprofd_prop); 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(` 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, system_file_type)
r_dir_file(heapprofd, apk_data_file) r_dir_file(heapprofd, apk_data_file)
r_dir_file(heapprofd, dalvikcache_data_file) r_dir_file(heapprofd, dalvikcache_data_file)
r_dir_file(heapprofd, vendor_file_type) 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(`{ never_profile_heap(`{
bpfloader bpfloader
init init
......
...@@ -60,6 +60,10 @@ allow isolated_app traced:fd use; ...@@ -60,6 +60,10 @@ allow isolated_app traced:fd use;
allow isolated_app traced_tmpfs:file { read write getattr map }; allow isolated_app traced_tmpfs:file { read write getattr map };
unix_socket_connect(isolated_app, traced_producer, traced) 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 ##### Neverallow
##### #####
......
...@@ -144,6 +144,10 @@ allow priv_app traced:fd use; ...@@ -144,6 +144,10 @@ allow priv_app traced:fd use;
allow priv_app traced_tmpfs:file { read write getattr map }; allow priv_app traced_tmpfs:file { read write getattr map };
unix_socket_connect(priv_app, traced_producer, traced) 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. # suppress denials for non-API accesses.
dontaudit priv_app exec_type:file getattr; dontaudit priv_app exec_type:file getattr;
dontaudit priv_app device:dir read; dontaudit priv_app device:dir read;
......
...@@ -123,6 +123,10 @@ allow untrusted_app_all traced:fd use; ...@@ -123,6 +123,10 @@ allow untrusted_app_all traced:fd use;
allow untrusted_app_all traced_tmpfs:file { read write getattr map }; allow untrusted_app_all traced_tmpfs:file { read write getattr map };
unix_socket_connect(untrusted_app_all, traced_producer, traced) 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 # allow untrusted apps to use UDP sockets provided by the system server but not
# modify them other than to connect # modify them other than to connect
allow untrusted_app_all system_server:udp_socket { allow untrusted_app_all system_server:udp_socket {
......
...@@ -647,31 +647,66 @@ define(`hal_attribute_hwservice', ` ...@@ -647,31 +647,66 @@ define(`hal_attribute_hwservice', `
################################### ###################################
# can_profile_heap(domain) # can_profile_heap(domain)
# never_profile_heap(domain) # Allow processes within the domain to have their heap profiled by heapprofd.
#
# 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.
# #
# 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', ` 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(` userdebug_or_eng(`
# RT signal for client initialization. # Allow central daemon to send signal for client initialization.
allow heapprofd $1:process signal; allow heapprofd $1:process signal;
# Connect to heapprofd service. # Allow connecting to the daemon.
unix_socket_connect($1, heapprofd, heapprofd) unix_socket_connect($1, heapprofd, heapprofd)
# To receive file descriptor. # Allow daemon to use the passed fds.
allow heapprofd $1:fd use; allow heapprofd $1:fd use;
# To read from the received file descriptors. # To read from the received file descriptors.
# /proc/[pid]/maps and /proc/[pid]/mem have the same SELinux label as the # /proc/[pid]/maps and /proc/[pid]/mem have the same SELinux label as the
# process they relate to. # process they relate to.
allow heapprofd $1:file r_file_perms; 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:dir r_dir_perms;
allow heapprofd $1:process signal;
') ')
') ')
###################################
# never_profile_heap(domain)
# Opt out of heap profiling by heapprofd.
define(`never_profile_heap', ` define(`never_profile_heap', `
neverallow heapprofd $1:file read; neverallow heapprofd $1:file read;
neverallow heapprofd $1:process signal; neverallow heapprofd $1:process signal;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment