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