diff options
author | Primiano Tucci <primiano@google.com> | 2023-06-13 13:17:15 +0100 |
---|---|---|
committer | Cherrypicker Worker <android-build-cherrypicker-worker@google.com> | 2023-06-13 15:51:36 +0000 |
commit | 72f3d346122320676cc61620d531f07cc467b59e (patch) | |
tree | fefad33ef3a1ec43e3e319dd7f53a84b39a3d98e | |
parent | 9bf4017e70115c08316b0c197972a1f736abcf25 (diff) | |
download | perfetto-72f3d346122320676cc61620d531f07cc467b59e.tar.gz |
traced: allow cross-session cloning for bugreports
Prior changed introduced generalized support for CloneSession
and rewrote the --save-for-bugreport feature on top of that.
Unfortunately we didn't notice that the CloneSession feature
is very strict on UID checking, enforcing that source and target
consumer UID match. This is good in general but not desirable
(And a regression) for the case of bugreports, where the two
uids differ.
In this case bugreport_score acts as an "I am fine exporting
this trace to other uids".
Bug: 286908237
Bug: 260112703
(cherry picked from https://android-review.googlesource.com/q/commit:5457722190e8cad7dddbedc71b8957ab45402a6c)
Merged-In: I99bd22aef0bfa402cb24559b8515c5bdf0b48ba9
Change-Id: I99bd22aef0bfa402cb24559b8515c5bdf0b48ba9
-rw-r--r-- | protos/perfetto/config/perfetto_config.proto | 11 | ||||
-rw-r--r-- | protos/perfetto/config/trace_config.proto | 11 | ||||
-rw-r--r-- | protos/perfetto/trace/perfetto_trace.proto | 11 | ||||
-rw-r--r-- | src/tracing/core/tracing_service_impl.cc | 7 | ||||
-rw-r--r-- | src/tracing/core/tracing_service_impl_unittest.cc | 58 |
5 files changed, 88 insertions, 10 deletions
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto index 85f1de780..5fbcf8dc2 100644 --- a/protos/perfetto/config/perfetto_config.proto +++ b/protos/perfetto/config/perfetto_config.proto @@ -2935,13 +2935,18 @@ message TraceConfig { // trace ends to notify it about the trace readiness. optional bool notify_traceur = 16; + // This field was introduced in Android S. // Android-only. If set to a value > 0, marks the trace session as a candidate // for being attached to a bugreport. This field effectively acts as a z-index // for bugreports. When Android's dumpstate runs perfetto // --save-for-bugreport, traced will pick the tracing session with the highest - // score (score <= 0 is ignored), will steal its contents, save the trace into - // a known path and stop prematurely. - // This field was introduced in Android S. + // score (score <= 0 is ignored) and: + // On Android S, T: will steal its contents, save the trace into + // a known path and stop prematurely. + // On Android U+: will create a read-only snapshot and save that into a known + // path, without stoppin the original tracing session. + // When this field is set the tracing session becomes eligible to be cloned + // by other UIDs. optional int32 bugreport_score = 30; // Triggers allow producers to start or stop the tracing session when an event diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto index 4fe3ea974..67c5f3d87 100644 --- a/protos/perfetto/config/trace_config.proto +++ b/protos/perfetto/config/trace_config.proto @@ -256,13 +256,18 @@ message TraceConfig { // trace ends to notify it about the trace readiness. optional bool notify_traceur = 16; + // This field was introduced in Android S. // Android-only. If set to a value > 0, marks the trace session as a candidate // for being attached to a bugreport. This field effectively acts as a z-index // for bugreports. When Android's dumpstate runs perfetto // --save-for-bugreport, traced will pick the tracing session with the highest - // score (score <= 0 is ignored), will steal its contents, save the trace into - // a known path and stop prematurely. - // This field was introduced in Android S. + // score (score <= 0 is ignored) and: + // On Android S, T: will steal its contents, save the trace into + // a known path and stop prematurely. + // On Android U+: will create a read-only snapshot and save that into a known + // path, without stoppin the original tracing session. + // When this field is set the tracing session becomes eligible to be cloned + // by other UIDs. optional int32 bugreport_score = 30; // Triggers allow producers to start or stop the tracing session when an event diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto index e886311d7..77ff04114 100644 --- a/protos/perfetto/trace/perfetto_trace.proto +++ b/protos/perfetto/trace/perfetto_trace.proto @@ -2935,13 +2935,18 @@ message TraceConfig { // trace ends to notify it about the trace readiness. optional bool notify_traceur = 16; + // This field was introduced in Android S. // Android-only. If set to a value > 0, marks the trace session as a candidate // for being attached to a bugreport. This field effectively acts as a z-index // for bugreports. When Android's dumpstate runs perfetto // --save-for-bugreport, traced will pick the tracing session with the highest - // score (score <= 0 is ignored), will steal its contents, save the trace into - // a known path and stop prematurely. - // This field was introduced in Android S. + // score (score <= 0 is ignored) and: + // On Android S, T: will steal its contents, save the trace into + // a known path and stop prematurely. + // On Android U+: will create a read-only snapshot and save that into a known + // path, without stoppin the original tracing session. + // When this field is set the tracing session becomes eligible to be cloned + // by other UIDs. optional int32 bugreport_score = 30; // Triggers allow producers to start or stop the tracing session when an event diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc index b32a65854..7c687f5d7 100644 --- a/src/tracing/core/tracing_service_impl.cc +++ b/src/tracing/core/tracing_service_impl.cc @@ -3580,8 +3580,13 @@ base::Status TracingServiceImpl::DoCloneSession(ConsumerEndpointImpl* consumer, "The consumer is already attached to another tracing session"); } - if (src->consumer_uid != consumer->uid_ && consumer->uid_ != 0) + // Skip the UID check for sessions marked with a bugreport_score > 0. + // Those sessions, by design, can be stolen by any other consumer for the + // sake of creating snapshots for bugreports. + if (src->config.bugreport_score() <= 0 && + src->consumer_uid != consumer->uid_ && consumer->uid_ != 0) { return PERFETTO_SVC_ERR("Not allowed to clone a session from another UID"); + } // First clone all TraceBuffer(s). This can fail because of ENOMEM. If it // happens bail out early before creating any session. diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc index fb175e990..9b6b6ae54 100644 --- a/src/tracing/core/tracing_service_impl_unittest.cc +++ b/src/tracing/core/tracing_service_impl_unittest.cc @@ -4529,6 +4529,64 @@ TEST_F(TracingServiceImplTest, CloneSession) { Property(&protos::gen::TraceUuid::lsb, Eq(clone_uuid.lsb())))))); } +// Test that a consumer cannot clone a session from a consumer with a different +// uid (unless it's marked as eligible for bugreport, see next test). +TEST_F(TracingServiceImplTest, CloneSessionAcrossUidDenied) { + // The consumer the creates the initial tracing session. + std::unique_ptr<MockConsumer> consumer = CreateMockConsumer(); + consumer->Connect(svc.get()); + + // The consumer that clones it and reads back the data. + std::unique_ptr<MockConsumer> consumer2 = CreateMockConsumer(); + consumer2->Connect(svc.get(), 1234); + + TraceConfig trace_config; + trace_config.add_buffers()->set_size_kb(32); + + consumer->EnableTracing(trace_config); + auto flush_request = consumer->Flush(); + ASSERT_TRUE(flush_request.WaitForReply()); + + auto clone_done = task_runner.CreateCheckpoint("clone_done"); + EXPECT_CALL(*consumer2, OnSessionCloned(_)) + .WillOnce(Invoke([clone_done](const Consumer::OnSessionClonedArgs& args) { + clone_done(); + ASSERT_FALSE(args.success); + ASSERT_TRUE(base::Contains(args.error, "session from another UID")); + })); + consumer2->CloneSession(1); + task_runner.RunUntilCheckpoint("clone_done"); +} + +// Test that a consumer can clone a session from a different uid if the trace is +// marked as eligible for bugreport. +TEST_F(TracingServiceImplTest, CloneSessionAcrossUidForBugreport) { + // The consumer the creates the initial tracing session. + std::unique_ptr<MockConsumer> consumer = CreateMockConsumer(); + consumer->Connect(svc.get()); + + // The consumer that clones it and reads back the data. + std::unique_ptr<MockConsumer> consumer2 = CreateMockConsumer(); + consumer2->Connect(svc.get(), 1234); + + TraceConfig trace_config; + trace_config.add_buffers()->set_size_kb(32); + trace_config.set_bugreport_score(1); + + consumer->EnableTracing(trace_config); + auto flush_request = consumer->Flush(); + ASSERT_TRUE(flush_request.WaitForReply()); + + auto clone_done = task_runner.CreateCheckpoint("clone_done"); + EXPECT_CALL(*consumer2, OnSessionCloned(_)) + .WillOnce(Invoke([clone_done](const Consumer::OnSessionClonedArgs& args) { + clone_done(); + ASSERT_TRUE(args.success); + })); + consumer2->CloneSession(1); + task_runner.RunUntilCheckpoint("clone_done"); +} + TEST_F(TracingServiceImplTest, InvalidBufferSizes) { std::unique_ptr<MockConsumer> consumer = CreateMockConsumer(); consumer->Connect(svc.get()); |