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/Alignment.h" 10 #include "mozilla/CheckedInt.h" 11 #include "mozilla/EndianUtils.h" 12 #include "mozilla/TypeTraits.h" 13 14 #include <stdlib.h> 15 16 #include <limits> 17 #include <string> 18 #include <algorithm> 19 20 #include "nsDebug.h" 21 22 //------------------------------------------------------------------------------ 23 24 static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t), 25 "Insufficient alignment"); 26 27 static const uint32_t kHeaderSegmentCapacity = 64; 28 29 static const uint32_t kDefaultSegmentCapacity = 4096; 30 31 static const char kBytePaddingMarker = char(0xbf); 32 33 namespace { 34 35 // We want to copy data to our payload as efficiently as possible. 36 // memcpy fits the bill for copying, but not all compilers or 37 // architectures support inlining memcpy from void*, which has unknown 38 // static alignment. However, we know that all the members of our 39 // payload will be aligned on memberAlignmentType boundaries. We 40 // therefore use that knowledge to construct a copier that will copy 41 // efficiently (via standard C++ assignment mechanisms) if the datatype 42 // needs that alignment or less, and memcpy otherwise. (The compiler 43 // may still inline memcpy, of course.) 44 45 template<typename T, size_t size, bool hasSufficientAlignment> 46 struct Copier 47 { 48 static void Copy(T* dest, const char* iter) { 49 memcpy(dest, iter, sizeof(T)); 50 } 51 }; 52 53 // Copying 64-bit quantities happens often enough and can easily be made 54 // worthwhile on 32-bit platforms, so handle it specially. Only do it 55 // if 64-bit types aren't sufficiently aligned; the alignment 56 // requirements for them vary between 32-bit platforms. 57 #ifndef HAVE_64BIT_BUILD 58 template<typename T> 59 struct Copier<T, sizeof(uint64_t), false> 60 { 61 static void Copy(T* dest, const char* iter) { 62 #if MOZ_LITTLE_ENDIAN 63 static const int loIndex = 0, hiIndex = 1; 64 #else 65 static const int loIndex = 1, hiIndex = 0; 66 #endif 67 static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*), 68 "Pointers have different alignments"); 69 const uint32_t* src = reinterpret_cast<const uint32_t*>(iter); 70 uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest); 71 uint32dest[loIndex] = src[loIndex]; 72 uint32dest[hiIndex] = src[hiIndex]; 73 } 74 }; 75 #endif 76 77 template<typename T, size_t size> 78 struct Copier<T, size, true> 79 { 80 static void Copy(T* dest, const char* iter) { 81 *dest = *reinterpret_cast<const T*>(iter); 82 } 83 }; 84 85 } // anonymous namespace 86 87 PickleIterator::PickleIterator(const Pickle& pickle) 88 : iter_(pickle.buffers_.Iter()) { 89 iter_.Advance(pickle.buffers_, pickle.header_size_); 90 } 91 92 template<typename T> 93 void 94 PickleIterator::CopyInto(T* dest) { 95 static_assert(mozilla::IsPod<T>::value, "Copied type must be a POD type"); 96 Copier<T, sizeof(T), (MOZ_ALIGNOF(T) <= sizeof(Pickle::memberAlignmentType))>::Copy(dest, iter_.Data()); 97 } 98 99 bool Pickle::IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const { 100 // Make sure we don't get into trouble where AlignInt(len) == 0. 101 MOZ_RELEASE_ASSERT(len < 64); 102 103 return iter.iter_.HasRoomFor(AlignInt(len)); 104 } 105 106 void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const { 107 // Make sure we don't get into trouble where AlignInt(bytes) == 0. 108 MOZ_RELEASE_ASSERT(bytes < 64); 109 110 iter->iter_.Advance(buffers_, AlignInt(bytes)); 111 } 112 113 // Payload is sizeof(Pickle::memberAlignmentType) aligned. 114 115 Pickle::Pickle(uint32_t header_size) 116 : buffers_(AlignInt(header_size), kHeaderSegmentCapacity, kDefaultSegmentCapacity), 117 header_(nullptr), 118 header_size_(AlignInt(header_size)) { 119 DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); 120 DCHECK(header_size_ <= kHeaderSegmentCapacity); 121 header_ = reinterpret_cast<Header*>(buffers_.Start()); 122 header_->payload_size = 0; 123 } 124 125 Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length) 126 : buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity), 127 header_(nullptr), 128 header_size_(AlignInt(header_size)) { 129 DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); 130 DCHECK(header_size <= kHeaderSegmentCapacity); 131 MOZ_RELEASE_ASSERT(header_size <= length); 132 133 header_ = reinterpret_cast<Header*>(buffers_.Start()); 134 memcpy(header_, data, length); 135 } 136 137 Pickle::Pickle(Pickle&& other) 138 : buffers_(mozilla::Move(other.buffers_)), 139 header_(other.header_), 140 header_size_(other.header_size_) { 141 other.header_ = nullptr; 142 } 143 144 Pickle::~Pickle() { 145 } 146 147 Pickle& Pickle::operator=(Pickle&& other) { 148 BufferList tmp = mozilla::Move(other.buffers_); 149 other.buffers_ = mozilla::Move(buffers_); 150 buffers_ = mozilla::Move(tmp); 151 152 //std::swap(buffers_, other.buffers_); 153 std::swap(header_, other.header_); 154 std::swap(header_size_, other.header_size_); 155 return *this; 156 } 157 158 bool Pickle::ReadBool(PickleIterator* iter, bool* result) const { 159 DCHECK(iter); 160 161 int tmp; 162 if (!ReadInt(iter, &tmp)) 163 return false; 164 DCHECK(0 == tmp || 1 == tmp); 165 *result = tmp ? true : false; 166 return true; 167 } 168 169 bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const { 170 DCHECK(iter); 171 172 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 173 return ReadBytesInto(iter, result, sizeof(*result)); 174 175 iter->CopyInto(result); 176 177 UpdateIter(iter, sizeof(*result)); 178 return true; 179 } 180 181 bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const { 182 DCHECK(iter); 183 184 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 185 return ReadBytesInto(iter, result, sizeof(*result)); 186 187 iter->CopyInto(result); 188 189 UpdateIter(iter, sizeof(*result)); 190 return true; 191 } 192 193 bool Pickle::ReadInt(PickleIterator* iter, int* result) const { 194 DCHECK(iter); 195 196 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 197 return ReadBytesInto(iter, result, sizeof(*result)); 198 199 iter->CopyInto(result); 200 201 UpdateIter(iter, sizeof(*result)); 202 return true; 203 } 204 205 // Always written as a 64-bit value since the size for this type can 206 // differ between architectures. 207 bool Pickle::ReadLong(PickleIterator* iter, long* result) const { 208 DCHECK(iter); 209 210 int64_t big_result = 0; 211 if (IteratorHasRoomFor(*iter, sizeof(big_result))) { 212 iter->CopyInto(&big_result); 213 UpdateIter(iter, sizeof(big_result)); 214 } else { 215 if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { 216 return false; 217 } 218 } 219 DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN); 220 *result = static_cast<long>(big_result); 221 222 return true; 223 } 224 225 // Always written as a 64-bit value since the size for this type can 226 // differ between architectures. 227 bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const { 228 DCHECK(iter); 229 230 uint64_t big_result = 0; 231 if (IteratorHasRoomFor(*iter, sizeof(big_result))) { 232 iter->CopyInto(&big_result); 233 UpdateIter(iter, sizeof(big_result)); 234 } else { 235 if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { 236 return false; 237 } 238 } 239 DCHECK(big_result <= ULONG_MAX); 240 *result = static_cast<unsigned long>(big_result); 241 242 return true; 243 } 244 245 bool Pickle::ReadLength(PickleIterator* iter, int* result) const { 246 if (!ReadInt(iter, result)) 247 return false; 248 return ((*result) >= 0); 249 } 250 251 // Always written as a 64-bit value since the size for this type can 252 // differ between architectures. 253 bool Pickle::ReadSize(PickleIterator* iter, size_t* result) const { 254 DCHECK(iter); 255 256 uint64_t big_result = 0; 257 if (IteratorHasRoomFor(*iter, sizeof(big_result))) { 258 iter->CopyInto(&big_result); 259 UpdateIter(iter, sizeof(big_result)); 260 } else { 261 if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { 262 return false; 263 } 264 } 265 DCHECK(big_result <= std::numeric_limits<size_t>::max()); 266 *result = static_cast<size_t>(big_result); 267 268 return true; 269 } 270 271 bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const { 272 DCHECK(iter); 273 274 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 275 return ReadBytesInto(iter, result, sizeof(*result)); 276 277 iter->CopyInto(result); 278 279 UpdateIter(iter, sizeof(*result)); 280 return true; 281 } 282 283 bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const { 284 DCHECK(iter); 285 286 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 287 return ReadBytesInto(iter, result, sizeof(*result)); 288 289 iter->CopyInto(result); 290 291 UpdateIter(iter, sizeof(*result)); 292 return true; 293 } 294 295 bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const { 296 DCHECK(iter); 297 298 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 299 return ReadBytesInto(iter, result, sizeof(*result)); 300 301 iter->CopyInto(result); 302 303 UpdateIter(iter, sizeof(*result)); 304 return true; 305 } 306 307 bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const { 308 DCHECK(iter); 309 310 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 311 return ReadBytesInto(iter, result, sizeof(*result)); 312 313 iter->CopyInto(result); 314 315 UpdateIter(iter, sizeof(*result)); 316 return true; 317 } 318 319 bool Pickle::ReadDouble(PickleIterator* iter, double* result) const { 320 DCHECK(iter); 321 322 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 323 return ReadBytesInto(iter, result, sizeof(*result)); 324 325 iter->CopyInto(result); 326 327 UpdateIter(iter, sizeof(*result)); 328 return true; 329 } 330 331 // Always written as a 64-bit value since the size for this type can 332 // differ between architectures. 333 bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const { 334 DCHECK(iter); 335 336 int64_t big_result = 0; 337 if (IteratorHasRoomFor(*iter, sizeof(big_result))) { 338 iter->CopyInto(&big_result); 339 UpdateIter(iter, sizeof(big_result)); 340 } else { 341 if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { 342 return false; 343 } 344 } 345 346 DCHECK(big_result <= std::numeric_limits<intptr_t>::max() && big_result >= std::numeric_limits<intptr_t>::min()); 347 *result = static_cast<intptr_t>(big_result); 348 349 return true; 350 } 351 352 bool Pickle::ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const { 353 DCHECK(iter); 354 355 if (!IteratorHasRoomFor(*iter, sizeof(*result))) 356 return ReadBytesInto(iter, result, sizeof(*result)); 357 358 iter->CopyInto(result); 359 360 UpdateIter(iter, sizeof(*result)); 361 return true; 362 } 363 364 bool Pickle::ReadString(PickleIterator* iter, std::string* result) const { 365 DCHECK(iter); 366 367 int len; 368 if (!ReadLength(iter, &len)) 369 return false; 370 371 auto chars = mozilla::MakeUnique<char[]>(len); 372 if (!ReadBytesInto(iter, chars.get(), len)) { 373 return false; 374 } 375 result->assign(chars.get(), len); 376 377 return true; 378 } 379 380 bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const { 381 DCHECK(iter); 382 383 int len; 384 if (!ReadLength(iter, &len)) 385 return false; 386 // Avoid integer multiplication overflow. 387 if (len > INT_MAX / static_cast<int>(sizeof(wchar_t))) 388 return false; 389 390 auto chars = mozilla::MakeUnique<wchar_t[]>(len); 391 if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) { 392 return false; 393 } 394 result->assign(chars.get(), len); 395 396 return true; 397 } 398 399 bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers, 400 uint32_t alignment) const 401 { 402 DCHECK(iter); 403 DCHECK(buffers); 404 DCHECK(alignment == 4 || alignment == 8); 405 DCHECK(intptr_t(header_) % alignment == 0); 406 407 if (AlignInt(length) < length) { 408 return false; 409 } 410 411 uint32_t padding_len = intptr_t(iter->iter_.Data()) % alignment; 412 if (!iter->iter_.AdvanceAcrossSegments(buffers_, padding_len)) { 413 return false; 414 } 415 416 bool success; 417 *buffers = const_cast<BufferList*>(&buffers_)->Extract(iter->iter_, length, &success); 418 if (!success) { 419 return false; 420 } 421 422 return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length); 423 } 424 425 bool Pickle::ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const { 426 if (AlignInt(length) < length) { 427 return false; 428 } 429 430 if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) { 431 return false; 432 } 433 434 return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length); 435 } 436 437 #ifdef MOZ_PICKLE_SENTINEL_CHECKING 438 bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const { 439 uint32_t found; 440 if (!ReadUInt32(iter, &found)) { 441 return false; 442 } 443 return found == sentinel; 444 } 445 446 bool Pickle::WriteSentinel(uint32_t sentinel) { 447 return WriteUInt32(sentinel); 448 } 449 #endif 450 451 void Pickle::EndRead(PickleIterator& iter) const { 452 DCHECK(iter.iter_.Done()); 453 } 454 455 void Pickle::BeginWrite(uint32_t length, uint32_t alignment) { 456 DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!"; 457 458 // write at an alignment-aligned offset from the beginning of the header 459 uint32_t offset = AlignInt(header_->payload_size); 460 uint32_t padding = (header_size_ + offset) % alignment; 461 uint32_t new_size = offset + padding + AlignInt(length); 462 MOZ_RELEASE_ASSERT(new_size >= header_->payload_size); 463 464 DCHECK(intptr_t(header_) % alignment == 0); 465 466 #ifdef ARCH_CPU_64_BITS 467 DCHECK_LE(length, std::numeric_limits<uint32_t>::max()); 468 #endif 469 470 if (padding) { 471 MOZ_RELEASE_ASSERT(padding <= 8); 472 static const char padding_data[8] = { 473 kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, 474 kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, 475 }; 476 buffers_.WriteBytes(padding_data, padding); 477 } 478 479 DCHECK((header_size_ + header_->payload_size + padding) % alignment == 0); 480 481 header_->payload_size = new_size; 482 } 483 484 void Pickle::EndWrite(uint32_t length) { 485 // Zero-pad to keep tools like purify from complaining about uninitialized 486 // memory. 487 uint32_t padding = AlignInt(length) - length; 488 if (padding) { 489 MOZ_RELEASE_ASSERT(padding <= 4); 490 static const char padding_data[4] = { 491 kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, 492 }; 493 buffers_.WriteBytes(padding_data, padding); 494 } 495 } 496 497 bool Pickle::WriteBytes(const void* data, uint32_t data_len, uint32_t alignment) { 498 DCHECK(alignment == 4 || alignment == 8); 499 DCHECK(intptr_t(header_) % alignment == 0); 500 501 BeginWrite(data_len, alignment); 502 503 buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len); 504 505 EndWrite(data_len); 506 return true; 507 } 508 509 bool Pickle::WriteString(const std::string& value) { 510 #ifdef MOZ_FAULTY 511 std::string v(value); 512 Singleton<mozilla::ipc::Faulty>::get()->FuzzString(v); 513 if (!WriteInt(static_cast<int>(v.size()))) 514 return false; 515 516 return WriteBytes(v.data(), static_cast<int>(v.size())); 517 #else 518 if (!WriteInt(static_cast<int>(value.size()))) 519 return false; 520 521 return WriteBytes(value.data(), static_cast<int>(value.size())); 522 #endif 523 } 524 525 bool Pickle::WriteWString(const std::wstring& value) { 526 #ifdef MOZ_FAULTY 527 std::wstring v(value); 528 Singleton<mozilla::ipc::Faulty>::get()->FuzzWString(v); 529 if (!WriteInt(static_cast<int>(v.size()))) 530 return false; 531 532 return WriteBytes(v.data(), 533 static_cast<int>(v.size() * sizeof(wchar_t))); 534 #else 535 if (!WriteInt(static_cast<int>(value.size()))) 536 return false; 537 538 return WriteBytes(value.data(), 539 static_cast<int>(value.size() * sizeof(wchar_t))); 540 #endif 541 } 542 543 bool Pickle::WriteData(const char* data, uint32_t length) { 544 #ifdef MOZ_FAULTY 545 std::string v(data, length); 546 Singleton<mozilla::ipc::Faulty>::get()->FuzzData(v, v.size()); 547 return WriteInt(v.size()) && WriteBytes(v.data(), v.size()); 548 #else 549 return WriteInt(length) && WriteBytes(data, length); 550 #endif 551 } 552 553 void Pickle::InputBytes(const char* data, uint32_t length) { 554 buffers_.WriteBytes(data, length); 555 } 556 557 int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) { 558 size_t pos = buffers_.Size() - offset; 559 BufferList::IterImpl iter(buffers_); 560 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos)); 561 return reinterpret_cast<int32_t*>(iter.Data()); 562 } 563 564 // static 565 uint32_t Pickle::MessageSize(uint32_t header_size, 566 const char* start, 567 const char* end) { 568 DCHECK(header_size == AlignInt(header_size)); 569 DCHECK(header_size <= static_cast<memberAlignmentType>(kHeaderSegmentCapacity)); 570 571 if (end < start) 572 return 0; 573 size_t length = static_cast<size_t>(end - start); 574 if (length < sizeof(Header)) 575 return 0; 576 577 const Header* hdr = reinterpret_cast<const Header*>(start); 578 if (length < header_size) 579 return 0; 580 581 mozilla::CheckedInt<uint32_t> sum(header_size); 582 sum += hdr->payload_size; 583 584 if (!sum.isValid()) 585 return 0; 586 587 return sum.value(); 588 } 589