pickle.cc (15980B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 #include "base/pickle.h" 8 9 #include "mozilla/CheckedInt.h" 10 #include "mozilla/EndianUtils.h" 11 #include "mozilla/Telemetry.h" 12 #include "mozilla/ipc/ProtocolUtils.h" 13 14 #include <stdlib.h> 15 16 #include <limits> 17 #include <string> 18 #include <algorithm> 19 #include <type_traits> 20 21 #include "nsDebug.h" 22 23 //------------------------------------------------------------------------------ 24 25 static_assert(alignof(Pickle::memberAlignmentType) >= alignof(uint32_t), 26 "Insufficient alignment"); 27 28 static const uint32_t kHeaderSegmentCapacity = 64; 29 30 static const uint32_t kDefaultSegmentCapacity = 4096; 31 32 static const char kBytePaddingMarker = char(0xbf); 33 34 namespace { 35 36 // We want to copy data to our payload as efficiently as possible. 37 // memcpy fits the bill for copying, but not all compilers or 38 // architectures support inlining memcpy from void*, which has unknown 39 // static alignment. However, we know that all the members of our 40 // payload will be aligned on memberAlignmentType boundaries. We 41 // therefore use that knowledge to construct a copier that will copy 42 // efficiently (via standard C++ assignment mechanisms) if the datatype 43 // needs that alignment or less, and memcpy otherwise. (The compiler 44 // may still inline memcpy, of course.) 45 46 template <typename T, size_t size, bool hasSufficientAlignment> 47 struct Copier { 48 static void Copy(T* dest, const char* iter) { memcpy(dest, iter, sizeof(T)); } 49 }; 50 51 // Copying 64-bit quantities happens often enough and can easily be made 52 // worthwhile on 32-bit platforms, so handle it specially. Only do it 53 // if 64-bit types aren't sufficiently aligned; the alignment 54 // requirements for them vary between 32-bit platforms. 55 #ifndef HAVE_64BIT_BUILD 56 template <typename T> 57 struct Copier<T, sizeof(uint64_t), false> { 58 static void Copy(T* dest, const char* iter) { 59 # if MOZ_LITTLE_ENDIAN 60 static const int loIndex = 0, hiIndex = 1; 61 # else 62 static const int loIndex = 1, hiIndex = 0; 63 # endif 64 static_assert(alignof(uint32_t*) == alignof(void*), 65 "Pointers have different alignments"); 66 const uint32_t* src = reinterpret_cast<const uint32_t*>(iter); 67 uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest); 68 uint32dest[loIndex] = src[loIndex]; 69 uint32dest[hiIndex] = src[hiIndex]; 70 } 71 }; 72 #endif 73 74 template <typename T, size_t size> 75 struct Copier<T, size, true> { 76 static void Copy(T* dest, const char* iter) { 77 // The pointer ought to be properly aligned. 78 DCHECK_EQ((((uintptr_t)iter) & (alignof(T) - 1)), 0); 79 *dest = *reinterpret_cast<const T*>(iter); 80 } 81 }; 82 83 } // anonymous namespace 84 85 PickleIterator::PickleIterator(const Pickle& pickle) 86 : iter_(pickle.buffers_.Iter()) { 87 iter_.Advance(pickle.buffers_, pickle.header_size_); 88 } 89 90 template <typename T> 91 void PickleIterator::CopyInto(T* dest) { 92 static_assert(std::is_trivially_copyable<T>::value, 93 "Copied type must be a POD type"); 94 Copier<T, sizeof(T), (alignof(T) <= sizeof(Pickle::memberAlignmentType))>:: 95 Copy(dest, iter_.Data()); 96 } 97 template void PickleIterator::CopyInto<char>(char*); 98 99 bool Pickle::IteratorHasRoomFor(const PickleIterator& iter, 100 uint32_t len) const { 101 // Make sure we don't get into trouble where AlignInt(len) == 0. 102 MOZ_RELEASE_ASSERT(len < 64); 103 104 return iter.iter_.HasRoomFor(AlignInt(len)); 105 } 106 107 bool Pickle::HasBytesAvailable(const PickleIterator* iter, uint32_t len) const { 108 return iter->iter_.HasBytesAvailable(buffers_, len); 109 } 110 111 void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const { 112 // Make sure we don't get into trouble where AlignInt(bytes) == 0. 113 MOZ_RELEASE_ASSERT(bytes < 64); 114 115 iter->iter_.Advance(buffers_, AlignInt(bytes)); 116 } 117 118 // Payload is sizeof(Pickle::memberAlignmentType) aligned. 119 120 Pickle::Pickle(uint32_t header_size, size_t segment_capacity) 121 : buffers_(AlignInt(header_size), 122 segment_capacity ? segment_capacity : kHeaderSegmentCapacity, 123 segment_capacity ? segment_capacity : kDefaultSegmentCapacity), 124 header_(nullptr), 125 header_size_(AlignInt(header_size)) { 126 DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); 127 DCHECK(header_size_ <= kHeaderSegmentCapacity); 128 header_ = reinterpret_cast<Header*>(buffers_.Start()); 129 memset(header_, 0, header_size_); 130 header_->payload_size = 0; 131 } 132 133 Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length) 134 : buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity), 135 header_(nullptr), 136 header_size_(AlignInt(header_size)) { 137 DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); 138 DCHECK(header_size <= kHeaderSegmentCapacity); 139 MOZ_RELEASE_ASSERT(header_size <= length); 140 141 header_ = reinterpret_cast<Header*>(buffers_.Start()); 142 memcpy(header_, data, length); 143 } 144 145 Pickle::Pickle(Pickle&& other) 146 : buffers_(std::move(other.buffers_)), 147 header_(other.header_), 148 header_size_(other.header_size_) { 149 other.header_ = nullptr; 150 } 151 152 Pickle::~Pickle() {} 153 154 Pickle& Pickle::operator=(Pickle&& other) { 155 BufferList tmp = std::move(other.buffers_); 156 other.buffers_ = std::move(buffers_); 157 buffers_ = std::move(tmp); 158 159 // std::swap(buffers_, other.buffers_); 160 std::swap(header_, other.header_); 161 std::swap(header_size_, other.header_size_); 162 return *this; 163 } 164 165 void Pickle::CopyFrom(const Pickle& other) { 166 MOZ_ALWAYS_TRUE(buffers_.CopyFrom(other.buffers_)); 167 MOZ_ASSERT(other.header_ == 168 reinterpret_cast<const Header*>(other.buffers_.Start())); 169 170 header_ = reinterpret_cast<Header*>(buffers_.Start()); 171 header_size_ = other.header_size_; 172 } 173 174 bool Pickle::ReadBool(PickleIterator* iter, bool* result) const { 175 int tmp; 176 if (!ReadScalar(iter, &tmp)) return false; 177 178 DCHECK(0 == tmp || 1 == tmp); 179 *result = tmp ? true : false; 180 181 return true; 182 } 183 184 bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const { 185 return ReadScalar(iter, result); 186 } 187 188 bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const { 189 return ReadScalar(iter, result); 190 } 191 192 bool Pickle::ReadInt(PickleIterator* iter, int* result) const { 193 return ReadScalar(iter, result); 194 } 195 196 // Always written as a 64-bit value since the size for this type can 197 // differ between architectures. 198 bool Pickle::ReadLong(PickleIterator* iter, long* result) const { 199 int64_t big_result; 200 if (!ReadScalar(iter, &big_result)) return false; 201 202 DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN); 203 *result = static_cast<long>(big_result); 204 205 return true; 206 } 207 208 // Always written as a 64-bit value since the size for this type can 209 // differ between architectures. 210 bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const { 211 uint64_t big_result; 212 if (!ReadScalar(iter, &big_result)) return false; 213 DCHECK(big_result <= ULONG_MAX); 214 *result = static_cast<unsigned long>(big_result); 215 216 return true; 217 } 218 219 bool Pickle::ReadLength(PickleIterator* iter, int* result) const { 220 if (!ReadScalar(iter, result)) return false; 221 return ((*result) >= 0); 222 } 223 224 bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const { 225 return ReadScalar(iter, result); 226 } 227 228 bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const { 229 return ReadScalar(iter, result); 230 } 231 232 bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const { 233 return ReadScalar(iter, result); 234 } 235 236 bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const { 237 return ReadScalar(iter, result); 238 } 239 240 bool Pickle::ReadDouble(PickleIterator* iter, double* result) const { 241 return ReadScalar(iter, result); 242 } 243 244 // Always written as a 64-bit value since the size for this type can 245 // differ between architectures. 246 bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const { 247 DCHECK(iter); 248 249 int64_t big_result; 250 if (!ReadScalar(iter, &big_result)) return false; 251 252 DCHECK(big_result <= std::numeric_limits<intptr_t>::max() && 253 big_result >= std::numeric_limits<intptr_t>::min()); 254 *result = static_cast<intptr_t>(big_result); 255 256 return true; 257 } 258 259 bool Pickle::ReadUnsignedChar(PickleIterator* iter, 260 unsigned char* result) const { 261 return ReadScalar(iter, result); 262 } 263 264 bool Pickle::ReadString(PickleIterator* iter, std::string* result) const { 265 DCHECK(iter); 266 267 int len; 268 if (!ReadLength(iter, &len)) return false; 269 270 auto chars = mozilla::MakeUnique<char[]>(len); 271 if (!ReadBytesInto(iter, chars.get(), len)) { 272 return false; 273 } 274 result->assign(chars.get(), len); 275 276 return true; 277 } 278 279 bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const { 280 DCHECK(iter); 281 282 int len; 283 if (!ReadLength(iter, &len)) return false; 284 // Avoid integer multiplication overflow. 285 if (len > INT_MAX / static_cast<int>(sizeof(wchar_t))) return false; 286 287 auto chars = mozilla::MakeUnique<wchar_t[]>(len); 288 if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) { 289 return false; 290 } 291 result->assign(chars.get(), len); 292 293 return true; 294 } 295 296 bool Pickle::ReadBytesInto(PickleIterator* iter, void* data, 297 uint32_t length) const { 298 if (AlignInt(length) < length) { 299 return false; 300 } 301 302 if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) { 303 return false; 304 } 305 306 return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length); 307 } 308 309 bool Pickle::IgnoreBytes(PickleIterator* iter, uint32_t length) const { 310 if (AlignInt(length) < length) { 311 return false; 312 } 313 314 return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length)); 315 } 316 317 #ifdef MOZ_PICKLE_SENTINEL_CHECKING 318 MOZ_NEVER_INLINE 319 bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const { 320 uint32_t found; 321 if (!ReadScalar(iter, &found)) { 322 return false; 323 } 324 return found == sentinel; 325 } 326 327 bool Pickle::IgnoreSentinel(PickleIterator* iter) const { 328 uint32_t found; 329 return ReadUInt32(iter, &found); 330 } 331 332 bool Pickle::WriteSentinel(uint32_t sentinel) { return WriteUInt32(sentinel); } 333 #endif 334 335 void Pickle::EndRead(PickleIterator& iter, uint32_t ipcMsgType) const { 336 // FIXME: Deal with the footer somehow... 337 // DCHECK(iter.iter_.Done()); 338 } 339 340 void Pickle::Truncate(PickleIterator* iter) { 341 size_t dropped = buffers_.Truncate(iter->iter_); 342 header_->payload_size -= dropped; 343 } 344 345 static const char kBytePaddingData[4] = { 346 kBytePaddingMarker, 347 kBytePaddingMarker, 348 kBytePaddingMarker, 349 kBytePaddingMarker, 350 }; 351 352 static void WritePadding(Pickle::BufferList& buffers, uint32_t padding) { 353 MOZ_RELEASE_ASSERT(padding <= 4); 354 if (padding) { 355 MOZ_ALWAYS_TRUE(buffers.WriteBytes(kBytePaddingData, padding)); 356 } 357 } 358 359 void Pickle::BeginWrite(uint32_t length) { 360 // write at an alignment-aligned offset from the beginning of the header 361 uint32_t offset = AlignInt(header_->payload_size); 362 uint32_t padding = (header_size_ + offset) % sizeof(memberAlignmentType); 363 uint32_t new_size = offset + padding + AlignInt(length); 364 MOZ_RELEASE_ASSERT(new_size >= header_->payload_size); 365 366 DCHECK(intptr_t(header_) % sizeof(memberAlignmentType) == 0); 367 368 #ifdef HAVE_64BIT_BUILD 369 DCHECK_LE(length, std::numeric_limits<uint32_t>::max()); 370 #endif 371 372 WritePadding(buffers_, padding); 373 374 DCHECK((header_size_ + header_->payload_size + padding) % 375 sizeof(memberAlignmentType) == 376 0); 377 378 header_->payload_size = new_size; 379 } 380 381 void Pickle::EndWrite(uint32_t length) { 382 uint32_t padding = AlignInt(length) - length; 383 WritePadding(buffers_, padding); 384 } 385 386 bool Pickle::WriteBool(bool value) { return WriteInt(value ? 1 : 0); } 387 388 bool Pickle::WriteInt16(int16_t value) { 389 return WriteBytes(&value, sizeof(value)); 390 } 391 392 bool Pickle::WriteUInt16(uint16_t value) { 393 return WriteBytes(&value, sizeof(value)); 394 } 395 396 bool Pickle::WriteInt(int value) { return WriteBytes(&value, sizeof(value)); } 397 398 bool Pickle::WriteLong(long value) { 399 // Always written as a 64-bit value since the size for this type can 400 // differ between architectures. 401 return WriteInt64(int64_t(value)); 402 } 403 404 bool Pickle::WriteULong(unsigned long value) { 405 // Always written as a 64-bit value since the size for this type can 406 // differ between architectures. 407 return WriteUInt64(uint64_t(value)); 408 } 409 410 bool Pickle::WriteInt32(int32_t value) { 411 return WriteBytes(&value, sizeof(value)); 412 } 413 414 bool Pickle::WriteUInt32(uint32_t value) { 415 return WriteBytes(&value, sizeof(value)); 416 } 417 418 bool Pickle::WriteInt64(int64_t value) { 419 return WriteBytes(&value, sizeof(value)); 420 } 421 422 bool Pickle::WriteUInt64(uint64_t value) { 423 return WriteBytes(&value, sizeof(value)); 424 } 425 426 bool Pickle::WriteDouble(double value) { 427 return WriteBytes(&value, sizeof(value)); 428 } 429 430 bool Pickle::WriteIntPtr(intptr_t value) { 431 // Always written as a 64-bit value since the size for this type can 432 // differ between architectures. 433 return WriteInt64(int64_t(value)); 434 } 435 436 bool Pickle::WriteUnsignedChar(unsigned char value) { 437 return WriteBytes(&value, sizeof(value)); 438 } 439 440 bool Pickle::WriteBytesZeroCopy(void* data, uint32_t data_len, 441 uint32_t capacity) { 442 BeginWrite(data_len); 443 444 uint32_t new_capacity = AlignInt(capacity); 445 #ifndef MOZ_MEMORY 446 if (new_capacity > capacity) { 447 // If the buffer we were given is not large enough to contain padding 448 // after the data, reallocate it to make it so. When using jemalloc, 449 // we're guaranteed the buffer size is going to be at least 4-bytes 450 // aligned, so we skip realloc altogether. Even with other allocators, 451 // the realloc is likely not necessary, but we don't take chances. 452 // At least with ASan, it does matter to realloc to inform ASan we're 453 // going to use more data from the buffer (and let it actually realloc 454 // if it needs to). 455 data = realloc(data, new_capacity); 456 } 457 #endif 458 459 // Shouldn't fail, because we're using InfallibleAllocPolicy. 460 MOZ_ALWAYS_TRUE(buffers_.WriteBytesZeroCopy(reinterpret_cast<char*>(data), 461 data_len, new_capacity)); 462 463 EndWrite(data_len); 464 return true; 465 } 466 467 bool Pickle::WriteBytes(const void* data, uint32_t data_len) { 468 BeginWrite(data_len); 469 470 MOZ_ALWAYS_TRUE( 471 buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len)); 472 473 EndWrite(data_len); 474 return true; 475 } 476 477 bool Pickle::WriteString(const std::string& value) { 478 if (!WriteInt(static_cast<int>(value.size()))) return false; 479 480 return WriteBytes(value.data(), static_cast<int>(value.size())); 481 } 482 483 bool Pickle::WriteWString(const std::wstring& value) { 484 if (!WriteInt(static_cast<int>(value.size()))) return false; 485 486 return WriteBytes(value.data(), 487 static_cast<int>(value.size() * sizeof(wchar_t))); 488 } 489 490 bool Pickle::WriteData(const char* data, uint32_t length) { 491 return WriteInt(length) && WriteBytes(data, length); 492 } 493 494 void Pickle::InputBytes(const char* data, uint32_t length) { 495 MOZ_ALWAYS_TRUE(buffers_.WriteBytes(data, length)); 496 } 497 498 int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) { 499 size_t pos = buffers_.Size() - offset; 500 BufferList::IterImpl iter(buffers_); 501 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos)); 502 return reinterpret_cast<int32_t*>(iter.Data()); 503 } 504 505 // static 506 uint32_t Pickle::MessageSize(uint32_t header_size, const char* start, 507 const char* end) { 508 DCHECK(header_size == AlignInt(header_size)); 509 DCHECK(header_size <= 510 static_cast<memberAlignmentType>(kHeaderSegmentCapacity)); 511 512 if (end < start) return 0; 513 size_t length = static_cast<size_t>(end - start); 514 if (length < sizeof(Header)) return 0; 515 516 const Header* hdr = reinterpret_cast<const Header*>(start); 517 if (length < header_size) return 0; 518 519 mozilla::CheckedInt<uint32_t> sum(header_size); 520 sum += hdr->payload_size; 521 522 if (!sum.isValid()) return 0; 523 524 return sum.value(); 525 }