Disallow extra guardrails profiles on traced_perf on user.
Test: atest TracedPerfCtsTest on flame-user
Test: atest TracedPerfCtsTest on flame-userdebug
Bug: 153139002
Change-Id: Ic0e4147d2438f7c364382e418f63bc75c401c065
diff --git a/Android.bp b/Android.bp
index d954988..eae5962 100644
--- a/Android.bp
+++ b/Android.bp
@@ -9360,6 +9360,7 @@
":perfetto_src_profiling_common_interner",
":perfetto_src_profiling_common_interning_output",
":perfetto_src_profiling_common_proc_utils",
+ ":perfetto_src_profiling_common_producer_support",
":perfetto_src_profiling_common_profiler_guardrails",
":perfetto_src_profiling_common_unwind_support",
":perfetto_src_profiling_perf_common_types",
@@ -9369,6 +9370,7 @@
":perfetto_src_profiling_perf_traced_perf_main",
":perfetto_src_profiling_perf_unwinding",
":perfetto_src_protozero_protozero",
+ ":perfetto_src_traced_probes_packages_list_packages_list_parser",
":perfetto_src_tracing_common",
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_core_service",
diff --git a/src/profiling/perf/BUILD.gn b/src/profiling/perf/BUILD.gn
index a1b19ba..044601a 100644
--- a/src/profiling/perf/BUILD.gn
+++ b/src/profiling/perf/BUILD.gn
@@ -63,6 +63,7 @@
"../common:interner",
"../common:interning_output",
"../common:proc_utils",
+ "../common:producer_support",
"../common:profiler_guardrails",
]
sources = [
diff --git a/src/profiling/perf/event_config.cc b/src/profiling/perf/event_config.cc
index 8051af7..58b41c5 100644
--- a/src/profiling/perf/event_config.cc
+++ b/src/profiling/perf/event_config.cc
@@ -110,12 +110,13 @@
const DataSourceConfig& ds_config) {
protos::pbzero::PerfEventConfig::Decoder event_config_pb(
ds_config.perf_event_config_raw());
- return EventConfig::Create(event_config_pb);
+ return EventConfig::Create(event_config_pb, ds_config);
}
// static
base::Optional<EventConfig> EventConfig::Create(
- const protos::pbzero::PerfEventConfig::Decoder& pb_config) {
+ const protos::pbzero::PerfEventConfig::Decoder& pb_config,
+ const DataSourceConfig& raw_ds_config) {
base::Optional<TargetFilter> filter = ParseTargetFilter(pb_config);
if (!filter.has_value())
return base::nullopt;
@@ -151,12 +152,14 @@
PERFETTO_DLOG("Capping samples (not records) per tick to [%" PRIu32 "]",
samples_per_tick_limit);
- return EventConfig(pb_config, sampling_frequency, ring_buffer_pages.value(),
- read_tick_period_ms, samples_per_tick_limit,
- remote_descriptor_timeout_ms, std::move(filter.value()));
+ return EventConfig(pb_config, raw_ds_config, sampling_frequency,
+ ring_buffer_pages.value(), read_tick_period_ms,
+ samples_per_tick_limit, remote_descriptor_timeout_ms,
+ std::move(filter.value()));
}
EventConfig::EventConfig(const protos::pbzero::PerfEventConfig::Decoder& cfg,
+ const DataSourceConfig& raw_ds_config,
uint32_t sampling_frequency,
uint32_t ring_buffer_pages,
uint32_t read_tick_period_ms,
@@ -170,7 +173,8 @@
target_filter_(std::move(target_filter)),
remote_descriptor_timeout_ms_(remote_descriptor_timeout_ms),
unwind_state_clear_period_ms_(cfg.unwind_state_clear_period_ms()),
- kernel_frames_(cfg.kernel_frames()) {
+ kernel_frames_(cfg.kernel_frames()),
+ raw_ds_config_(raw_ds_config) /* copy */ {
auto& pe = perf_event_attr_;
pe.size = sizeof(perf_event_attr);
diff --git a/src/profiling/perf/event_config.h b/src/profiling/perf/event_config.h
index c667d39..046e523 100644
--- a/src/profiling/perf/event_config.h
+++ b/src/profiling/perf/event_config.h
@@ -49,7 +49,8 @@
public:
static base::Optional<EventConfig> Create(const DataSourceConfig& ds_config);
static base::Optional<EventConfig> Create(
- const protos::pbzero::PerfEventConfig::Decoder& ds_config);
+ const protos::pbzero::PerfEventConfig::Decoder& pb_config,
+ const DataSourceConfig& raw_ds_config);
uint32_t target_all_cpus() const { return target_all_cpus_; }
uint32_t ring_buffer_pages() const { return ring_buffer_pages_; }
@@ -68,8 +69,11 @@
return const_cast<perf_event_attr*>(&perf_event_attr_);
}
+ const DataSourceConfig& raw_ds_config() const { return raw_ds_config_; }
+
private:
EventConfig(const protos::pbzero::PerfEventConfig::Decoder& cfg,
+ const DataSourceConfig& raw_ds_config,
uint32_t sampling_frequency,
uint32_t ring_buffer_pages,
uint32_t read_tick_period_ms,
@@ -105,6 +109,9 @@
// If true, include kernel frames in the callstacks.
const bool kernel_frames_;
+
+ // The raw data source config, as a pbzero-generated C++ class.
+ const DataSourceConfig raw_ds_config_;
};
} // namespace profiling
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index 26a3380..236b57b 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -37,6 +37,7 @@
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "src/profiling/common/callstack_trie.h"
#include "src/profiling/common/proc_utils.h"
+#include "src/profiling/common/producer_support.h"
#include "src/profiling/common/profiler_guardrails.h"
#include "src/profiling/common/unwind_support.h"
#include "src/profiling/perf/common_types.h"
@@ -218,7 +219,7 @@
protos::pbzero::PerfEventConfig::Decoder event_config_pb(
config.perf_event_config_raw());
base::Optional<EventConfig> event_config =
- EventConfig::Create(event_config_pb);
+ EventConfig::Create(event_config_pb, config);
if (!event_config.has_value()) {
PERFETTO_ELOG("PerfEventConfig rejected.");
return;
@@ -529,6 +530,7 @@
// Note: first-fit makes descriptor request fulfillment not true FIFO. But the
// edge-cases where it matters are very unlikely.
void PerfProducer::OnProcDescriptors(pid_t pid,
+ uid_t uid,
base::ScopedFile maps_fd,
base::ScopedFile mem_fd) {
// Find first-fit data source that requested descriptors for the process.
@@ -538,6 +540,13 @@
if (proc_status_it == ds.process_states.end())
continue;
+ if (!CanProfile(ds.event_config.raw_ds_config(), uid)) {
+ PERFETTO_DLOG("Not profileable: pid [%d], uid [%d] for DS [%zu]",
+ static_cast<int>(pid), static_cast<int>(uid),
+ static_cast<size_t>(it.first));
+ continue;
+ }
+
// Match against either resolving, or expired state. In the latter
// case, it means that the async response was slow enough that we've marked
// the lookup as expired (but can now recover for future samples).
diff --git a/src/profiling/perf/perf_producer.h b/src/profiling/perf/perf_producer.h
index 5241cb8..cdc2a80 100644
--- a/src/profiling/perf/perf_producer.h
+++ b/src/profiling/perf/perf_producer.h
@@ -84,6 +84,7 @@
// ProcDescriptorDelegate impl:
void OnProcDescriptors(pid_t pid,
+ uid_t uid,
base::ScopedFile maps_fd,
base::ScopedFile mem_fd) override;
diff --git a/src/profiling/perf/proc_descriptors.cc b/src/profiling/perf/proc_descriptors.cc
index 4df8814..b03d5d0 100644
--- a/src/profiling/perf/proc_descriptors.cc
+++ b/src/profiling/perf/proc_descriptors.cc
@@ -36,26 +36,43 @@
}
void DirectDescriptorGetter::GetDescriptorsForPid(pid_t pid) {
- char buf[128] = {};
- snprintf(buf, sizeof(buf), "/proc/%d/maps", pid);
- auto maps_fd = base::ScopedFile{open(buf, O_RDONLY | O_CLOEXEC)};
+ char dir_buf[128] = {};
+ snprintf(dir_buf, sizeof(dir_buf), "/proc/%d", pid);
+ auto dir_fd =
+ base::ScopedFile(open(dir_buf, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (!dir_fd) {
+ if (errno != ENOENT) // not surprising if the process has quit
+ PERFETTO_PLOG("Failed to open [%s]", dir_buf);
+
+ return;
+ }
+
+ struct stat stat_buf;
+ if (fstat(dir_fd.get(), &stat_buf) == -1) {
+ PERFETTO_PLOG("Failed to stat [%s]", dir_buf);
+ return;
+ }
+
+ auto maps_fd =
+ base::ScopedFile{openat(dir_fd.get(), "maps", O_RDONLY | O_CLOEXEC)};
if (!maps_fd) {
if (errno != ENOENT) // not surprising if the process has quit
- PERFETTO_PLOG("Failed to open [%s]", buf);
+ PERFETTO_PLOG("Failed to open %s/maps", dir_buf);
return;
}
- snprintf(buf, sizeof(buf), "/proc/%d/mem", pid);
- auto mem_fd = base::ScopedFile{open(buf, O_RDONLY | O_CLOEXEC)};
+ auto mem_fd =
+ base::ScopedFile{openat(dir_fd.get(), "mem", O_RDONLY | O_CLOEXEC)};
if (!mem_fd) {
if (errno != ENOENT) // not surprising if the process has quit
- PERFETTO_PLOG("Failed to open [%s]", buf);
+ PERFETTO_PLOG("Failed to open %s/mem", dir_buf);
return;
}
- delegate_->OnProcDescriptors(pid, std::move(maps_fd), std::move(mem_fd));
+ delegate_->OnProcDescriptors(pid, stat_buf.st_uid, std::move(maps_fd),
+ std::move(mem_fd));
}
// AndroidRemoteDescriptorGetter:
@@ -117,8 +134,8 @@
if (!received_bytes)
return;
- delegate_->OnProcDescriptors(self->peer_pid_linux(), std::move(fds[0]),
- std::move(fds[1]));
+ delegate_->OnProcDescriptors(self->peer_pid_linux(), self->peer_uid_posix(),
+ std::move(fds[0]), std::move(fds[1]));
}
} // namespace perfetto
diff --git a/src/profiling/perf/proc_descriptors.h b/src/profiling/perf/proc_descriptors.h
index e524b17..8846c01 100644
--- a/src/profiling/perf/proc_descriptors.h
+++ b/src/profiling/perf/proc_descriptors.h
@@ -32,6 +32,7 @@
class ProcDescriptorDelegate {
public:
virtual void OnProcDescriptors(pid_t pid,
+ uid_t uid,
base::ScopedFile maps_fd,
base::ScopedFile mem_fd) = 0;
diff --git a/test/cts/traced_perf_test_cts.cc b/test/cts/traced_perf_test_cts.cc
index df6a6b3..ba6bd0d 100644
--- a/test/cts/traced_perf_test_cts.cc
+++ b/test/cts/traced_perf_test_cts.cc
@@ -18,6 +18,8 @@
#include <sys/system_properties.h>
#include <sys/types.h>
+#include <string>
+
#include "perfetto/base/logging.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "src/base/test/test_task_runner.h"
@@ -43,7 +45,21 @@
return std::string(buf) == "1";
}
-std::vector<protos::gen::TracePacket> ProfileSystemWide(std::string app_name) {
+std::string RandomSessionName() {
+ std::random_device rd;
+ std::default_random_engine generator(rd());
+ std::uniform_int_distribution<char> distribution('a', 'z');
+
+ constexpr size_t kSessionNameLen = 20;
+ std::string result(kSessionNameLen, '\0');
+ for (size_t i = 0; i < kSessionNameLen; ++i)
+ result[i] = distribution(generator);
+ return result;
+}
+
+std::vector<protos::gen::TracePacket> ProfileSystemWide(
+ std::string app_name,
+ bool enable_extra_guardrails = false) {
base::TestTaskRunner task_runner;
// (re)start the target app's main activity
@@ -65,6 +81,8 @@
trace_config.add_buffers()->set_size_kb(20 * 1024);
trace_config.set_duration_ms(3000);
trace_config.set_data_source_stop_timeout_ms(8000);
+ trace_config.set_enable_extra_guardrails(enable_extra_guardrails);
+ trace_config.set_unique_session_name(RandomSessionName().c_str());
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("linux.perf");
@@ -155,6 +173,24 @@
StopApp(app_name);
}
+TEST(TracedPerfCtsTest, SystemWideDebuggableAppExtraGuardrails) {
+ if (!HasPerfLsmHooks())
+ GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
+
+ std::string app_name = "android.perfetto.cts.app.debuggable";
+ const auto& packets =
+ ProfileSystemWide(app_name, /*enable_extra_guardrails=*/true);
+ int app_pid = PidForProcessName(app_name);
+ ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+ if (IsDebuggableBuild())
+ AssertHasSampledStacksForPid(packets, app_pid);
+ else
+ AssertNoStacksForPid(packets, app_pid);
+ PERFETTO_CHECK(IsAppRunning(app_name));
+ StopApp(app_name);
+}
+
TEST(TracedPerfCtsTest, SystemWideProfileableApp) {
if (!HasPerfLsmHooks())
GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
@@ -169,6 +205,24 @@
StopApp(app_name);
}
+TEST(TracedPerfCtsTest, SystemWideProfileableAppExtraGuardrails) {
+ if (!HasPerfLsmHooks())
+ GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
+
+ std::string app_name = "android.perfetto.cts.app.profileable";
+ const auto& packets =
+ ProfileSystemWide(app_name, /*enable_extra_guardrails=*/true);
+ int app_pid = PidForProcessName(app_name);
+ ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+ if (IsDebuggableBuild())
+ AssertHasSampledStacksForPid(packets, app_pid);
+ else
+ AssertNoStacksForPid(packets, app_pid);
+ PERFETTO_CHECK(IsAppRunning(app_name));
+ StopApp(app_name);
+}
+
TEST(TracedPerfCtsTest, SystemWideReleaseApp) {
if (!HasPerfLsmHooks())
GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
@@ -187,5 +241,24 @@
StopApp(app_name);
}
+TEST(TracedPerfCtsTest, SystemWideReleaseAppExtraGuardrails) {
+ if (!HasPerfLsmHooks())
+ GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
+
+ std::string app_name = "android.perfetto.cts.app.release";
+ const auto& packets =
+ ProfileSystemWide(app_name, /*enable_extra_guardrails=*/true);
+ int app_pid = PidForProcessName(app_name);
+ ASSERT_GT(app_pid, 0) << "failed to find pid for target process";
+
+ if (IsDebuggableBuild())
+ AssertHasSampledStacksForPid(packets, app_pid);
+ else
+ AssertNoStacksForPid(packets, app_pid);
+
+ PERFETTO_CHECK(IsAppRunning(app_name));
+ StopApp(app_name);
+}
+
} // namespace
} // namespace perfetto