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