Merge "init: add builtin check for perf_event LSM hooks" am: cdebef1d2b am: bc1dba9d4f am: c5ad8f4933
Change-Id: Ib6c8729ba30a85b9ddaff6afc50eb3b48353e789
diff --git a/init/init.cpp b/init/init.cpp
index 5f97e44..a25bf6c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -730,8 +730,8 @@
}
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
-
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+ am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
diff --git a/init/security.cpp b/init/security.cpp
index 586d0c7..6cbe642 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -18,14 +18,19 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/perf_event.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
#include <unistd.h>
#include <fstream>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
using android::base::unique_fd;
+using android::base::SetProperty;
namespace android {
namespace init {
@@ -197,5 +202,61 @@
return {};
}
+// Test for whether the kernel has SELinux hooks for the perf_event_open()
+// syscall. If the hooks are present, we can stop using the other permission
+// mechanism (perf_event_paranoid sysctl), and use only the SELinux policy to
+// control access to the syscall. The hooks are expected on all Android R
+// release kernels, but might be absent on devices that upgrade while keeping an
+// older kernel.
+//
+// There is no direct/synchronous way of finding out that a syscall failed due
+// to SELinux. Therefore we test for a combination of a success and a failure
+// that are explained by the platform's SELinux policy for the "init" domain:
+// * cpu-scoped perf_event is allowed
+// * ioctl() on the event fd is disallowed with EACCES
+//
+// Since init has CAP_SYS_ADMIN, these tests are not affected by the system-wide
+// perf_event_paranoid sysctl.
+//
+// If the SELinux hooks are detected, a special sysprop
+// (sys.init.perf_lsm_hooks) is set, which translates to a modification of
+// perf_event_paranoid (through init.rc sysprop actions).
+//
+// TODO(b/137092007): this entire test can be removed once the platform stops
+// supporting kernels that precede the perf_event_open hooks (Android common
+// kernels 4.4 and 4.9).
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&) {
+ // Use a trivial event that will be configured, but not started.
+ struct perf_event_attr pe = {
+ .type = PERF_TYPE_SOFTWARE,
+ .size = sizeof(struct perf_event_attr),
+ .config = PERF_COUNT_SW_TASK_CLOCK,
+ .disabled = 1,
+ .exclude_kernel = 1,
+ };
+
+ // Open the above event targeting cpu 0. (EINTR not possible.)
+ unique_fd fd(static_cast<int>(syscall(__NR_perf_event_open, &pe, /*pid=*/-1,
+ /*cpu=*/0,
+ /*group_fd=*/-1, /*flags=*/0)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Unexpected perf_event_open error";
+ return {};
+ }
+
+ int ioctl_ret = ioctl(fd, PERF_EVENT_IOC_RESET);
+ if (ioctl_ret != -1) {
+ // Success implies that the kernel doesn't have the hooks.
+ return {};
+ } else if (errno != EACCES) {
+ PLOG(ERROR) << "Unexpected perf_event ioctl error";
+ return {};
+ }
+
+ // Conclude that the SELinux hooks are present.
+ SetProperty("sys.init.perf_lsm_hooks", "1");
+ return {};
+}
+
} // namespace init
} // namespace android
diff --git a/init/security.h b/init/security.h
index b081a05..43c2739 100644
--- a/init/security.h
+++ b/init/security.h
@@ -29,6 +29,7 @@
Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
Result<void> SetKptrRestrictAction(const BuiltinArguments&);
+Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&);
} // namespace init
} // namespace android
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0b89909..6a6169c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -945,14 +945,33 @@
on property:sys.sysctl.tcp_def_init_rwnd=*
write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}
-on property:security.perf_harden=0
+# perf_event_open syscall security:
+# Newer kernels have the ability to control the use of the syscall via SELinux
+# hooks. init tests for this, and sets sys_init.perf_lsm_hooks to 1 if the
+# kernel has the hooks. In this case, the system-wide perf_event_paranoid
+# sysctl is set to -1 (unrestricted use), and the SELinux policy is used for
+# controlling access. On older kernels, the paranoid value is the only means of
+# controlling access. It is normally 3 (allow only root), but the shell user
+# can lower it to 1 (allowing thread-scoped pofiling) via security.perf_harden.
+on property:sys.init.perf_lsm_hooks=1
+ write /proc/sys/kernel/perf_event_paranoid -1
+on property:security.perf_harden=0 && property:sys.init.perf_lsm_hooks=""
write /proc/sys/kernel/perf_event_paranoid 1
+on property:security.perf_harden=1 && property:sys.init.perf_lsm_hooks=""
+ write /proc/sys/kernel/perf_event_paranoid 3
+
+# Additionally, simpleperf profiler uses debug.* and security.perf_harden
+# sysprops to be able to indirectly set these sysctls.
+on property:security.perf_harden=0
write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
-
+# Default values.
on property:security.perf_harden=1
- write /proc/sys/kernel/perf_event_paranoid 3
+ write /proc/sys/kernel/perf_event_max_sample_rate 100000
+ write /proc/sys/kernel/perf_cpu_time_max_percent 25
+ write /proc/sys/kernel/perf_event_mlock_kb 516
+
# on shutdown
# In device's init.rc, this trigger can be used to do device-specific actions