• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/ipc/host_impl.h"
18 
19 #include <algorithm>
20 #include <cinttypes>
21 #include <utility>
22 
23 #include "perfetto/base/build_config.h"
24 #include "perfetto/base/task_runner.h"
25 #include "perfetto/base/time.h"
26 #include "perfetto/ext/base/crash_keys.h"
27 #include "perfetto/ext/base/utils.h"
28 #include "perfetto/ext/ipc/service.h"
29 #include "perfetto/ext/ipc/service_descriptor.h"
30 
31 #include "protos/perfetto/ipc/wire_protocol.gen.h"
32 
33 // TODO(primiano): put limits on #connections/uid and req. queue (b/69093705).
34 
35 namespace perfetto {
36 namespace ipc {
37 
38 namespace {
39 
40 constexpr base::SockFamily kHostSockFamily =
41     kUseTCPSocket ? base::SockFamily::kInet : base::SockFamily::kUnix;
42 
43 base::CrashKey g_crash_key_uid("ipc_uid");
44 
GetPosixPeerUid(base::UnixSocket * sock)45 uid_t GetPosixPeerUid(base::UnixSocket* sock) {
46 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || \
47     PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
48   base::ignore_result(sock);
49   // Unsupported. Must be != kInvalidUid or the PacketValidator will fail.
50   return 0;
51 #else
52   return sock->peer_uid_posix();
53 #endif
54 }
55 
GetLinuxPeerPid(base::UnixSocket * sock)56 pid_t GetLinuxPeerPid(base::UnixSocket* sock) {
57 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
58     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
59   return sock->peer_pid_linux();
60 #else
61   base::ignore_result(sock);
62   return base::kInvalidPid;  // Unsupported.
63 #endif
64 }
65 
66 }  // namespace
67 
68 // static
CreateInstance(const char * socket_name,base::TaskRunner * task_runner)69 std::unique_ptr<Host> Host::CreateInstance(const char* socket_name,
70                                            base::TaskRunner* task_runner) {
71   std::unique_ptr<HostImpl> host(new HostImpl(socket_name, task_runner));
72   if (!host->sock() || !host->sock()->is_listening())
73     return nullptr;
74   return std::unique_ptr<Host>(std::move(host));
75 }
76 
77 // static
CreateInstance(base::ScopedSocketHandle socket_fd,base::TaskRunner * task_runner)78 std::unique_ptr<Host> Host::CreateInstance(base::ScopedSocketHandle socket_fd,
79                                            base::TaskRunner* task_runner) {
80   std::unique_ptr<HostImpl> host(
81       new HostImpl(std::move(socket_fd), task_runner));
82   if (!host->sock() || !host->sock()->is_listening())
83     return nullptr;
84   return std::unique_ptr<Host>(std::move(host));
85 }
86 
87 // static
CreateInstance_Fuchsia(base::TaskRunner * task_runner)88 std::unique_ptr<Host> Host::CreateInstance_Fuchsia(
89     base::TaskRunner* task_runner) {
90   return std::unique_ptr<HostImpl>(new HostImpl(task_runner));
91 }
92 
HostImpl(base::ScopedSocketHandle socket_fd,base::TaskRunner * task_runner)93 HostImpl::HostImpl(base::ScopedSocketHandle socket_fd,
94                    base::TaskRunner* task_runner)
95     : task_runner_(task_runner), weak_ptr_factory_(this) {
96   PERFETTO_DCHECK_THREAD(thread_checker_);
97   sock_ = base::UnixSocket::Listen(std::move(socket_fd), this, task_runner_,
98                                    kHostSockFamily, base::SockType::kStream);
99 }
100 
HostImpl(const char * socket_name,base::TaskRunner * task_runner)101 HostImpl::HostImpl(const char* socket_name, base::TaskRunner* task_runner)
102     : task_runner_(task_runner), weak_ptr_factory_(this) {
103   PERFETTO_DCHECK_THREAD(thread_checker_);
104   sock_ = base::UnixSocket::Listen(socket_name, this, task_runner_,
105                                    kHostSockFamily, base::SockType::kStream);
106   if (!sock_) {
107     PERFETTO_PLOG("Failed to create %s", socket_name);
108   }
109 }
110 
HostImpl(base::TaskRunner * task_runner)111 HostImpl::HostImpl(base::TaskRunner* task_runner)
112     : task_runner_(task_runner), weak_ptr_factory_(this) {
113   PERFETTO_DCHECK_THREAD(thread_checker_);
114 }
115 
116 HostImpl::~HostImpl() = default;
117 
ExposeService(std::unique_ptr<Service> service)118 bool HostImpl::ExposeService(std::unique_ptr<Service> service) {
119   PERFETTO_DCHECK_THREAD(thread_checker_);
120   const std::string& service_name = service->GetDescriptor().service_name;
121   if (GetServiceByName(service_name)) {
122     PERFETTO_DLOG("Duplicate ExposeService(): %s", service_name.c_str());
123     return false;
124   }
125   ServiceID sid = ++last_service_id_;
126   ExposedService exposed_service(sid, service_name, std::move(service));
127   services_.emplace(sid, std::move(exposed_service));
128   return true;
129 }
130 
AdoptConnectedSocket_Fuchsia(base::ScopedSocketHandle connected_socket,std::function<bool (int)> send_fd_cb)131 void HostImpl::AdoptConnectedSocket_Fuchsia(
132     base::ScopedSocketHandle connected_socket,
133     std::function<bool(int)> send_fd_cb) {
134   PERFETTO_DCHECK_THREAD(thread_checker_);
135   PERFETTO_DCHECK(connected_socket);
136   // Should not be used in conjunction with listen sockets.
137   PERFETTO_DCHECK(!sock_);
138 
139   auto unix_socket = base::UnixSocket::AdoptConnected(
140       std::move(connected_socket), this, task_runner_, kHostSockFamily,
141       base::SockType::kStream);
142 
143   auto* unix_socket_ptr = unix_socket.get();
144   OnNewIncomingConnection(nullptr, std::move(unix_socket));
145   ClientConnection* client_connection = clients_by_socket_[unix_socket_ptr];
146   client_connection->send_fd_cb_fuchsia = std::move(send_fd_cb);
147   PERFETTO_DCHECK(client_connection->send_fd_cb_fuchsia);
148 }
149 
SetSocketSendTimeoutMs(uint32_t timeout_ms)150 void HostImpl::SetSocketSendTimeoutMs(uint32_t timeout_ms) {
151   PERFETTO_DCHECK_THREAD(thread_checker_);
152   // Should be less than the watchdog period (30s).
153   socket_tx_timeout_ms_ = timeout_ms;
154 }
155 
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> new_conn)156 void HostImpl::OnNewIncomingConnection(
157     base::UnixSocket*,
158     std::unique_ptr<base::UnixSocket> new_conn) {
159   PERFETTO_DCHECK_THREAD(thread_checker_);
160   std::unique_ptr<ClientConnection> client(new ClientConnection());
161   ClientID client_id = ++last_client_id_;
162   clients_by_socket_[new_conn.get()] = client.get();
163   client->id = client_id;
164   client->sock = std::move(new_conn);
165   client->sock->SetTxTimeout(socket_tx_timeout_ms_);
166   clients_[client_id] = std::move(client);
167 }
168 
OnDataAvailable(base::UnixSocket * sock)169 void HostImpl::OnDataAvailable(base::UnixSocket* sock) {
170   PERFETTO_DCHECK_THREAD(thread_checker_);
171   auto it = clients_by_socket_.find(sock);
172   if (it == clients_by_socket_.end())
173     return;
174   ClientConnection* client = it->second;
175   BufferedFrameDeserializer& frame_deserializer = client->frame_deserializer;
176 
177   auto peer_uid = GetPosixPeerUid(client->sock.get());
178   auto scoped_key = g_crash_key_uid.SetScoped(static_cast<int64_t>(peer_uid));
179 
180   size_t rsize;
181   do {
182     auto buf = frame_deserializer.BeginReceive();
183     base::ScopedFile fd;
184     rsize = client->sock->Receive(buf.data, buf.size, &fd);
185     if (fd) {
186       PERFETTO_DCHECK(!client->received_fd);
187       client->received_fd = std::move(fd);
188     }
189     if (!frame_deserializer.EndReceive(rsize))
190       return OnDisconnect(client->sock.get());
191   } while (rsize > 0);
192 
193   for (;;) {
194     std::unique_ptr<Frame> frame = frame_deserializer.PopNextFrame();
195     if (!frame)
196       break;
197     OnReceivedFrame(client, *frame);
198   }
199 }
200 
OnReceivedFrame(ClientConnection * client,const Frame & req_frame)201 void HostImpl::OnReceivedFrame(ClientConnection* client,
202                                const Frame& req_frame) {
203   if (req_frame.has_msg_bind_service())
204     return OnBindService(client, req_frame);
205   if (req_frame.has_msg_invoke_method())
206     return OnInvokeMethod(client, req_frame);
207 
208   PERFETTO_DLOG("Received invalid RPC frame from client %" PRIu64, client->id);
209   Frame reply_frame;
210   reply_frame.set_request_id(req_frame.request_id());
211   reply_frame.mutable_msg_request_error()->set_error("unknown request");
212   SendFrame(client, reply_frame);
213 }
214 
OnBindService(ClientConnection * client,const Frame & req_frame)215 void HostImpl::OnBindService(ClientConnection* client, const Frame& req_frame) {
216   // Binding a service doesn't do anything major. It just returns back the
217   // service id and its method map.
218   const Frame::BindService& req = req_frame.msg_bind_service();
219   Frame reply_frame;
220   reply_frame.set_request_id(req_frame.request_id());
221   auto* reply = reply_frame.mutable_msg_bind_service_reply();
222   const ExposedService* service = GetServiceByName(req.service_name());
223   if (service) {
224     reply->set_success(true);
225     reply->set_service_id(service->id);
226     uint32_t method_id = 1;  // method ids start at index 1.
227     for (const auto& desc_method : service->instance->GetDescriptor().methods) {
228       Frame::BindServiceReply::MethodInfo* method_info = reply->add_methods();
229       method_info->set_name(desc_method.name);
230       method_info->set_id(method_id++);
231     }
232   }
233   SendFrame(client, reply_frame);
234 }
235 
OnInvokeMethod(ClientConnection * client,const Frame & req_frame)236 void HostImpl::OnInvokeMethod(ClientConnection* client,
237                               const Frame& req_frame) {
238   const Frame::InvokeMethod& req = req_frame.msg_invoke_method();
239   Frame reply_frame;
240   RequestID request_id = req_frame.request_id();
241   reply_frame.set_request_id(request_id);
242   reply_frame.mutable_msg_invoke_method_reply()->set_success(false);
243   auto svc_it = services_.find(req.service_id());
244   if (svc_it == services_.end())
245     return SendFrame(client, reply_frame);  // |success| == false by default.
246 
247   Service* service = svc_it->second.instance.get();
248   const ServiceDescriptor& svc = service->GetDescriptor();
249   const auto& methods = svc.methods;
250   const uint32_t method_id = req.method_id();
251   if (method_id == 0 || method_id > methods.size())
252     return SendFrame(client, reply_frame);
253 
254   const ServiceDescriptor::Method& method = methods[method_id - 1];
255   std::unique_ptr<ProtoMessage> decoded_req_args(
256       method.request_proto_decoder(req.args_proto()));
257   if (!decoded_req_args)
258     return SendFrame(client, reply_frame);
259 
260   Deferred<ProtoMessage> deferred_reply;
261   base::WeakPtr<HostImpl> host_weak_ptr = weak_ptr_factory_.GetWeakPtr();
262   ClientID client_id = client->id;
263 
264   if (!req.drop_reply()) {
265     deferred_reply.Bind([host_weak_ptr, client_id,
266                          request_id](AsyncResult<ProtoMessage> reply) {
267       if (!host_weak_ptr)
268         return;  // The reply came too late, the HostImpl has gone.
269       host_weak_ptr->ReplyToMethodInvocation(client_id, request_id,
270                                              std::move(reply));
271     });
272   }
273 
274   auto peer_uid = GetPosixPeerUid(client->sock.get());
275   auto scoped_key = g_crash_key_uid.SetScoped(static_cast<int64_t>(peer_uid));
276   service->client_info_ =
277       ClientInfo(client->id, peer_uid, GetLinuxPeerPid(client->sock.get()));
278   service->received_fd_ = &client->received_fd;
279   method.invoker(service, *decoded_req_args, std::move(deferred_reply));
280   service->received_fd_ = nullptr;
281   service->client_info_ = ClientInfo();
282 }
283 
ReplyToMethodInvocation(ClientID client_id,RequestID request_id,AsyncResult<ProtoMessage> reply)284 void HostImpl::ReplyToMethodInvocation(ClientID client_id,
285                                        RequestID request_id,
286                                        AsyncResult<ProtoMessage> reply) {
287   auto client_iter = clients_.find(client_id);
288   if (client_iter == clients_.end())
289     return;  // client has disconnected by the time we got the async reply.
290 
291   ClientConnection* client = client_iter->second.get();
292   Frame reply_frame;
293   reply_frame.set_request_id(request_id);
294 
295   // TODO(fmayer): add a test to guarantee that the reply is consumed within the
296   // same call stack and not kept around. ConsumerIPCService::OnTraceData()
297   // relies on this behavior.
298   auto* reply_frame_data = reply_frame.mutable_msg_invoke_method_reply();
299   reply_frame_data->set_has_more(reply.has_more());
300   if (reply.success()) {
301     std::string reply_proto = reply->SerializeAsString();
302     reply_frame_data->set_reply_proto(reply_proto);
303     reply_frame_data->set_success(true);
304   }
305   SendFrame(client, reply_frame, reply.fd());
306 }
307 
308 // static
SendFrame(ClientConnection * client,const Frame & frame,int fd)309 void HostImpl::SendFrame(ClientConnection* client, const Frame& frame, int fd) {
310   auto peer_uid = GetPosixPeerUid(client->sock.get());
311   auto scoped_key = g_crash_key_uid.SetScoped(static_cast<int64_t>(peer_uid));
312 
313   std::string buf = BufferedFrameDeserializer::Serialize(frame);
314 
315   // On Fuchsia, |send_fd_cb_fuchsia_| is used to send the FD to the client
316   // and therefore must be set.
317   PERFETTO_DCHECK(!PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) ||
318                   client->send_fd_cb_fuchsia);
319   if (client->send_fd_cb_fuchsia && fd != base::ScopedFile::kInvalid) {
320     if (!client->send_fd_cb_fuchsia(fd)) {
321       client->sock->Shutdown(true);
322       return;
323     }
324     fd = base::ScopedFile::kInvalid;
325   }
326 
327   // When a new Client connects in OnNewClientConnection we set a timeout on
328   // Send (see call to SetTxTimeout).
329   //
330   // The old behaviour was to do a blocking I/O call, which caused crashes from
331   // misbehaving producers (see b/169051440).
332   bool res = client->sock->Send(buf.data(), buf.size(), fd);
333   // If we timeout |res| will be false, but the UnixSocket will have called
334   // UnixSocket::ShutDown() and thus |is_connected()| is false.
335   PERFETTO_CHECK(res || !client->sock->is_connected());
336 }
337 
OnDisconnect(base::UnixSocket * sock)338 void HostImpl::OnDisconnect(base::UnixSocket* sock) {
339   PERFETTO_DCHECK_THREAD(thread_checker_);
340   auto it = clients_by_socket_.find(sock);
341   if (it == clients_by_socket_.end())
342     return;
343   ClientID client_id = it->second->id;
344 
345   ClientInfo client_info(client_id, GetPosixPeerUid(sock),
346                          GetLinuxPeerPid(sock));
347   clients_by_socket_.erase(it);
348   PERFETTO_DCHECK(clients_.count(client_id));
349   clients_.erase(client_id);
350 
351   for (const auto& service_it : services_) {
352     Service& service = *service_it.second.instance;
353     service.client_info_ = client_info;
354     service.OnClientDisconnected();
355     service.client_info_ = ClientInfo();
356   }
357 }
358 
GetServiceByName(const std::string & name)359 const HostImpl::ExposedService* HostImpl::GetServiceByName(
360     const std::string& name) {
361   // This could be optimized by using another map<name,ServiceID>. However this
362   // is used only by Bind/ExposeService that are quite rare (once per client
363   // connection and once per service instance), not worth it.
364   for (const auto& it : services_) {
365     if (it.second.name == name)
366       return &it.second;
367   }
368   return nullptr;
369 }
370 
ExposedService(ServiceID id_,const std::string & name_,std::unique_ptr<Service> instance_)371 HostImpl::ExposedService::ExposedService(ServiceID id_,
372                                          const std::string& name_,
373                                          std::unique_ptr<Service> instance_)
374     : id(id_), name(name_), instance(std::move(instance_)) {}
375 
376 HostImpl::ExposedService::ExposedService(ExposedService&&) noexcept = default;
377 HostImpl::ExposedService& HostImpl::ExposedService::operator=(
378     HostImpl::ExposedService&&) = default;
379 HostImpl::ExposedService::~ExposedService() = default;
380 
381 HostImpl::ClientConnection::~ClientConnection() = default;
382 
383 }  // namespace ipc
384 }  // namespace perfetto
385