1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ipc/ipc_message.h"
6
7#include <limits.h>
8#include <stddef.h>
9#include <stdint.h>
10
11#include "base/atomic_sequence_num.h"
12#include "base/logging.h"
13#include "build/build_config.h"
14#include "ipc/attachment_broker.h"
15#include "ipc/ipc_message_attachment.h"
16#include "ipc/ipc_message_attachment_set.h"
17#include "ipc/placeholder_brokerable_attachment.h"
18
19#if defined(OS_POSIX)
20#include "base/file_descriptor_posix.h"
21#include "ipc/ipc_platform_file_attachment_posix.h"
22#endif
23
24namespace {
25
26base::StaticAtomicSequenceNumber g_ref_num;
27
28// Create a reference number for identifying IPC messages in traces. The return
29// values has the reference number stored in the upper 24 bits, leaving the low
30// 8 bits set to 0 for use as flags.
31inline uint32_t GetRefNumUpper24() {
32  base::trace_event::TraceLog* trace_log =
33      base::trace_event::TraceLog::GetInstance();
34  uint32_t pid = trace_log ? trace_log->process_id() : 0;
35  uint32_t count = g_ref_num.GetNext();
36  // The 24 bit hash is composed of 14 bits of the count and 10 bits of the
37  // Process ID. With the current trace event buffer cap, the 14-bit count did
38  // not appear to wrap during a trace. Note that it is not a big deal if
39  // collisions occur, as this is only used for debugging and trace analysis.
40  return ((pid << 14) | (count & 0x3fff)) << 8;
41}
42
43}  // namespace
44
45namespace IPC {
46
47//------------------------------------------------------------------------------
48
49Message::~Message() {
50}
51
52Message::Message() : base::Pickle(sizeof(Header)) {
53  header()->routing = header()->type = 0;
54  header()->flags = GetRefNumUpper24();
55#if USE_ATTACHMENT_BROKER
56  header()->num_brokered_attachments = 0;
57#endif
58#if defined(OS_POSIX)
59  header()->num_fds = 0;
60  header()->pad = 0;
61#endif
62  Init();
63}
64
65Message::Message(int32_t routing_id, uint32_t type, PriorityValue priority)
66    : base::Pickle(sizeof(Header)) {
67  header()->routing = routing_id;
68  header()->type = type;
69  DCHECK((priority & 0xffffff00) == 0);
70  header()->flags = priority | GetRefNumUpper24();
71#if USE_ATTACHMENT_BROKER
72  header()->num_brokered_attachments = 0;
73#endif
74#if defined(OS_POSIX)
75  header()->num_fds = 0;
76  header()->pad = 0;
77#endif
78  Init();
79}
80
81Message::Message(const char* data, int data_len)
82    : base::Pickle(data, data_len) {
83  Init();
84}
85
86Message::Message(const Message& other) : base::Pickle(other) {
87  Init();
88  attachment_set_ = other.attachment_set_;
89  sender_pid_ = other.sender_pid_;
90}
91
92void Message::Init() {
93  dispatch_error_ = false;
94  sender_pid_ = base::kNullProcessId;
95#ifdef IPC_MESSAGE_LOG_ENABLED
96  received_time_ = 0;
97  dont_log_ = false;
98  log_data_ = NULL;
99#endif
100}
101
102Message& Message::operator=(const Message& other) {
103  *static_cast<base::Pickle*>(this) = other;
104  attachment_set_ = other.attachment_set_;
105  sender_pid_ = other.sender_pid_;
106  return *this;
107}
108
109void Message::SetHeaderValues(int32_t routing, uint32_t type, uint32_t flags) {
110  // This should only be called when the message is already empty.
111  DCHECK(payload_size() == 0);
112
113  header()->routing = routing;
114  header()->type = type;
115  header()->flags = flags;
116}
117
118void Message::EnsureMessageAttachmentSet() {
119  if (attachment_set_.get() == NULL)
120    attachment_set_ = new MessageAttachmentSet;
121}
122
123#ifdef IPC_MESSAGE_LOG_ENABLED
124void Message::set_sent_time(int64_t time) {
125  DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0);
126  header()->flags |= HAS_SENT_TIME_BIT;
127  WriteInt64(time);
128}
129
130int64_t Message::sent_time() const {
131  if ((header()->flags & HAS_SENT_TIME_BIT) == 0)
132    return 0;
133
134  const char* data = end_of_payload();
135  data -= sizeof(int64_t);
136  return *(reinterpret_cast<const int64_t*>(data));
137}
138
139void Message::set_received_time(int64_t time) const {
140  received_time_ = time;
141}
142#endif
143
144Message::NextMessageInfo::NextMessageInfo()
145    : message_size(0), message_found(false), pickle_end(nullptr),
146      message_end(nullptr) {}
147Message::NextMessageInfo::~NextMessageInfo() {}
148
149Message::SerializedAttachmentIds
150Message::SerializedIdsOfBrokerableAttachments() {
151  DCHECK(HasBrokerableAttachments());
152  std::vector<scoped_refptr<IPC::BrokerableAttachment>> attachments(
153      attachment_set_->GetBrokerableAttachments());
154  CHECK_LE(attachments.size(), std::numeric_limits<size_t>::max() /
155                                   BrokerableAttachment::kNonceSize);
156  size_t size = attachments.size() * BrokerableAttachment::kNonceSize;
157  char* buffer = static_cast<char*>(malloc(size));
158  for (size_t i = 0; i < attachments.size(); ++i) {
159    char* start_range = buffer + i * BrokerableAttachment::kNonceSize;
160    BrokerableAttachment::AttachmentId id = attachments[i]->GetIdentifier();
161    id.SerializeToBuffer(start_range, BrokerableAttachment::kNonceSize);
162  }
163  SerializedAttachmentIds ids;
164  ids.buffer = buffer;
165  ids.size = size;
166  return ids;
167}
168
169// static
170void Message::FindNext(const char* range_start,
171                       const char* range_end,
172                       NextMessageInfo* info) {
173  DCHECK(info);
174  info->message_found = false;
175  info->message_size = 0;
176
177  size_t pickle_size = 0;
178  if (!base::Pickle::PeekNext(sizeof(Header),
179                              range_start, range_end, &pickle_size))
180    return;
181
182  bool have_entire_pickle =
183      static_cast<size_t>(range_end - range_start) >= pickle_size;
184
185#if USE_ATTACHMENT_BROKER
186  // TODO(dskiba): determine message_size when entire pickle is not available
187
188  if (!have_entire_pickle)
189    return;
190
191  const char* pickle_end = range_start + pickle_size;
192
193  // The data is not copied.
194  Message message(range_start, static_cast<int>(pickle_size));
195  size_t num_attachments = message.header()->num_brokered_attachments;
196
197  // Check for possible overflows.
198  size_t max_size_t = std::numeric_limits<size_t>::max();
199  if (num_attachments >= max_size_t / BrokerableAttachment::kNonceSize)
200    return;
201
202  size_t attachment_length = num_attachments * BrokerableAttachment::kNonceSize;
203  if (pickle_size > max_size_t - attachment_length)
204    return;
205
206  // Check whether the range includes the attachments.
207  size_t buffer_length = static_cast<size_t>(range_end - range_start);
208  if (buffer_length < attachment_length + pickle_size)
209    return;
210
211  for (size_t i = 0; i < num_attachments; ++i) {
212    const char* attachment_start =
213        pickle_end + i * BrokerableAttachment::kNonceSize;
214    BrokerableAttachment::AttachmentId id(attachment_start,
215                                          BrokerableAttachment::kNonceSize);
216    info->attachment_ids.push_back(id);
217  }
218  info->message_end =
219      pickle_end + num_attachments * BrokerableAttachment::kNonceSize;
220  info->message_size = info->message_end - range_start;
221#else
222  info->message_size = pickle_size;
223
224  if (!have_entire_pickle)
225    return;
226
227  const char* pickle_end = range_start + pickle_size;
228
229  info->message_end = pickle_end;
230#endif  // USE_ATTACHMENT_BROKER
231
232  info->pickle_end = pickle_end;
233  info->message_found = true;
234}
235
236bool Message::AddPlaceholderBrokerableAttachmentWithId(
237    BrokerableAttachment::AttachmentId id) {
238  scoped_refptr<PlaceholderBrokerableAttachment> attachment(
239      new PlaceholderBrokerableAttachment(id));
240  return attachment_set()->AddAttachment(attachment);
241}
242
243bool Message::WriteAttachment(
244    scoped_refptr<base::Pickle::Attachment> attachment) {
245  bool brokerable;
246  size_t index;
247  bool success = attachment_set()->AddAttachment(
248      make_scoped_refptr(static_cast<MessageAttachment*>(attachment.get())),
249      &index, &brokerable);
250  DCHECK(success);
251
252  // NOTE: If you add more data to the pickle, make sure to update
253  // PickleSizer::AddAttachment.
254
255  // Write the type of descriptor.
256  WriteBool(brokerable);
257
258  // Write the index of the descriptor so that we don't have to
259  // keep the current descriptor as extra decoding state when deserialising.
260  WriteInt(static_cast<int>(index));
261
262#if USE_ATTACHMENT_BROKER
263  if (brokerable)
264    header()->num_brokered_attachments++;
265#endif
266
267  return success;
268}
269
270bool Message::ReadAttachment(
271    base::PickleIterator* iter,
272    scoped_refptr<base::Pickle::Attachment>* attachment) const {
273  bool brokerable;
274  if (!iter->ReadBool(&brokerable))
275    return false;
276
277  int index;
278  if (!iter->ReadInt(&index))
279    return false;
280
281  MessageAttachmentSet* attachment_set = attachment_set_.get();
282  if (!attachment_set)
283    return false;
284
285  *attachment = brokerable
286                    ? attachment_set->GetBrokerableAttachmentAt(index)
287                    : attachment_set->GetNonBrokerableAttachmentAt(index);
288
289  return nullptr != attachment->get();
290}
291
292bool Message::HasAttachments() const {
293  return attachment_set_.get() && !attachment_set_->empty();
294}
295
296bool Message::HasMojoHandles() const {
297  return attachment_set_.get() && attachment_set_->num_mojo_handles() > 0;
298}
299
300bool Message::HasBrokerableAttachments() const {
301  return attachment_set_.get() &&
302         attachment_set_->num_brokerable_attachments() > 0;
303}
304
305}  // namespace IPC
306