• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
6 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <limits>
12 #include <new>
13 
14 #include "base/component_export.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "mojo/public/c/system/macros.h"
18 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
19 #include "mojo/public/cpp/bindings/lib/buffer.h"
20 #include "mojo/public/cpp/bindings/lib/serialization_util.h"
21 #include "mojo/public/cpp/bindings/lib/template_util.h"
22 #include "mojo/public/cpp/bindings/lib/validate_params.h"
23 #include "mojo/public/cpp/bindings/lib/validation_context.h"
24 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
25 #include "mojo/public/cpp/bindings/lib/validation_util.h"
26 
27 namespace mojo {
28 namespace internal {
29 
30 template <typename K, typename V>
31 class Map_Data;
32 
33 COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE)
34 std::string MakeMessageWithArrayIndex(const char* message,
35                                       size_t size,
36                                       size_t index);
37 
38 COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE)
39 std::string MakeMessageWithExpectedArraySize(const char* message,
40                                              size_t size,
41                                              size_t expected_size);
42 
43 template <typename T>
44 struct ArrayDataTraits {
45   using StorageType = T;
46   using Ref = T&;
47   using ConstRef = const T&;
48 
49   static const uint32_t kMaxNumElements =
50       (std::numeric_limits<uint32_t>::max() - sizeof(ArrayHeader)) /
51       sizeof(StorageType);
52 
GetStorageSizeArrayDataTraits53   static uint32_t GetStorageSize(uint32_t num_elements) {
54     DCHECK(num_elements <= kMaxNumElements);
55     return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements;
56   }
ToRefArrayDataTraits57   static Ref ToRef(StorageType* storage, size_t offset) {
58     return storage[offset];
59   }
ToConstRefArrayDataTraits60   static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
61     return storage[offset];
62   }
63 };
64 
65 // Specialization of Arrays for bools, optimized for space. It has the
66 // following differences from a generalized Array:
67 // * Each element takes up a single bit of memory.
68 // * Accessing a non-const single element uses a helper class |BitRef|, which
69 // emulates a reference to a bool.
70 template <>
71 struct ArrayDataTraits<bool> {
72   // Helper class to emulate a reference to a bool, used for direct element
73   // access.
74   class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) BitRef {
75    public:
76     ~BitRef();
77     BitRef& operator=(bool value);
78     BitRef& operator=(const BitRef& value);
79     operator bool() const;
80 
81    private:
82     friend struct ArrayDataTraits<bool>;
83     BitRef(uint8_t* storage, uint8_t mask);
84     BitRef();
85     uint8_t* storage_;
86     uint8_t mask_;
87   };
88 
89   // Because each element consumes only 1/8 byte.
90   static const uint32_t kMaxNumElements = std::numeric_limits<uint32_t>::max();
91 
92   using StorageType = uint8_t;
93   using Ref = BitRef;
94   using ConstRef = bool;
95 
96   static uint32_t GetStorageSize(uint32_t num_elements) {
97     return sizeof(ArrayHeader) + ((num_elements + 7) / 8);
98   }
99   static BitRef ToRef(StorageType* storage, size_t offset) {
100     return BitRef(&storage[offset / 8], 1 << (offset % 8));
101   }
102   static bool ToConstRef(const StorageType* storage, size_t offset) {
103     return (storage[offset / 8] & (1 << (offset % 8))) != 0;
104   }
105 };
106 
107 // What follows is code to support the serialization/validation of
108 // Array_Data<T>. There are four interesting cases: arrays of primitives,
109 // arrays of handles/interfaces, arrays of objects and arrays of unions.
110 // Arrays of objects are represented as arrays of pointers to objects. Arrays
111 // of unions are inlined so they are not pointers, but comparing with primitives
112 // they require more work for serialization/validation.
113 //
114 // TODO(yzshen): Validation code should be organzied in a way similar to
115 // Serializer<>, or merged into it. It should be templatized with the mojo
116 // data view type instead of the data type, that way we can use MojomTypeTraits
117 // to determine the categories.
118 
119 template <typename T, bool is_union, bool is_handle_or_interface>
120 struct ArraySerializationHelper;
121 
122 template <typename T>
123 struct ArraySerializationHelper<T, false, false> {
124   using ElementType = typename ArrayDataTraits<T>::StorageType;
125 
126   static bool ValidateElements(const ArrayHeader* header,
127                                const ElementType* elements,
128                                ValidationContext* validation_context,
129                                const ContainerValidateParams* validate_params) {
130     DCHECK(!validate_params->element_is_nullable)
131         << "Primitive type should be non-nullable";
132     DCHECK(!validate_params->element_validate_params)
133         << "Primitive type should not have array validate params";
134 
135     if (!validate_params->validate_enum_func)
136       return true;
137 
138     // Enum validation.
139     for (uint32_t i = 0; i < header->num_elements; ++i) {
140       if (!validate_params->validate_enum_func(elements[i], validation_context))
141         return false;
142     }
143     return true;
144   }
145 };
146 
147 template <typename T>
148 struct ArraySerializationHelper<T, false, true> {
149   using ElementType = typename ArrayDataTraits<T>::StorageType;
150 
151   static bool ValidateElements(const ArrayHeader* header,
152                                const ElementType* elements,
153                                ValidationContext* validation_context,
154                                const ContainerValidateParams* validate_params) {
155     DCHECK(!validate_params->element_validate_params)
156         << "Handle or interface type should not have array validate params";
157 
158     for (uint32_t i = 0; i < header->num_elements; ++i) {
159       if (!validate_params->element_is_nullable &&
160           !IsHandleOrInterfaceValid(elements[i])) {
161         static const ValidationError kError =
162             std::is_same<T, Interface_Data>::value ||
163                     std::is_same<T, Handle_Data>::value
164                 ? VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE
165                 : VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID;
166         ReportValidationError(
167             validation_context, kError,
168             MakeMessageWithArrayIndex(
169                 "invalid handle or interface ID in array expecting valid "
170                 "handles or interface IDs",
171                 header->num_elements, i)
172                 .c_str());
173         return false;
174       }
175       if (!ValidateHandleOrInterface(elements[i], validation_context))
176         return false;
177     }
178     return true;
179   }
180 };
181 
182 template <typename T>
183 struct ArraySerializationHelper<Pointer<T>, false, false> {
184   using ElementType = typename ArrayDataTraits<Pointer<T>>::StorageType;
185 
186   static bool ValidateElements(const ArrayHeader* header,
187                                const ElementType* elements,
188                                ValidationContext* validation_context,
189                                const ContainerValidateParams* validate_params) {
190     for (uint32_t i = 0; i < header->num_elements; ++i) {
191       if (!validate_params->element_is_nullable && !elements[i].offset) {
192         ReportValidationError(
193             validation_context,
194             VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
195             MakeMessageWithArrayIndex("null in array expecting valid pointers",
196                                       header->num_elements,
197                                       i).c_str());
198         return false;
199       }
200       if (!ValidateCaller<T>::Run(elements[i], validation_context,
201                                   validate_params->element_validate_params)) {
202         return false;
203       }
204     }
205     return true;
206   }
207 
208  private:
209   template <typename U,
210             bool is_array_or_map = IsSpecializationOf<Array_Data, U>::value ||
211                                    IsSpecializationOf<Map_Data, U>::value>
212   struct ValidateCaller {
213     static bool Run(const Pointer<U>& data,
214                     ValidationContext* validation_context,
215                     const ContainerValidateParams* validate_params) {
216       DCHECK(!validate_params)
217           << "Struct type should not have array validate params";
218 
219       return ValidateStruct(data, validation_context);
220     }
221   };
222 
223   template <typename U>
224   struct ValidateCaller<U, true> {
225     static bool Run(const Pointer<U>& data,
226                     ValidationContext* validation_context,
227                     const ContainerValidateParams* validate_params) {
228       return ValidateContainer(data, validation_context, validate_params);
229     }
230   };
231 };
232 
233 template <typename U>
234 struct ArraySerializationHelper<U, true, false> {
235   using ElementType = typename ArrayDataTraits<U>::StorageType;
236 
237   static bool ValidateElements(const ArrayHeader* header,
238                                const ElementType* elements,
239                                ValidationContext* validation_context,
240                                const ContainerValidateParams* validate_params) {
241     for (uint32_t i = 0; i < header->num_elements; ++i) {
242       if (!validate_params->element_is_nullable && elements[i].is_null()) {
243         ReportValidationError(
244             validation_context,
245             VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
246             MakeMessageWithArrayIndex("null in array expecting valid unions",
247                                       header->num_elements, i)
248                 .c_str());
249         return false;
250       }
251       if (!ValidateInlinedUnion(elements[i], validation_context))
252         return false;
253     }
254     return true;
255   }
256 };
257 
258 template <typename T>
259 class Array_Data {
260  public:
261   using Traits = ArrayDataTraits<T>;
262   using StorageType = typename Traits::StorageType;
263   using Ref = typename Traits::Ref;
264   using ConstRef = typename Traits::ConstRef;
265   using Helper = ArraySerializationHelper<
266       T,
267       IsUnionDataType<T>::value,
268       std::is_same<T, AssociatedInterface_Data>::value ||
269           std::is_same<T, AssociatedEndpointHandle_Data>::value ||
270           std::is_same<T, Interface_Data>::value ||
271           std::is_same<T, Handle_Data>::value>;
272   using Element = T;
273 
274   class BufferWriter {
275    public:
276     BufferWriter() = default;
277 
278     void Allocate(size_t num_elements, Buffer* buffer) {
279       if (num_elements > Traits::kMaxNumElements)
280         return;
281 
282       uint32_t num_bytes =
283           Traits::GetStorageSize(static_cast<uint32_t>(num_elements));
284       buffer_ = buffer;
285       index_ = buffer_->Allocate(num_bytes);
286       new (data())
287           Array_Data<T>(num_bytes, static_cast<uint32_t>(num_elements));
288     }
289 
290     bool is_null() const { return !buffer_; }
291     Array_Data<T>* data() {
292       DCHECK(!is_null());
293       return buffer_->Get<Array_Data<T>>(index_);
294     }
295     Array_Data<T>* operator->() { return data(); }
296 
297    private:
298     Buffer* buffer_ = nullptr;
299     size_t index_ = 0;
300 
301     DISALLOW_COPY_AND_ASSIGN(BufferWriter);
302   };
303 
304   static bool Validate(const void* data,
305                        ValidationContext* validation_context,
306                        const ContainerValidateParams* validate_params) {
307     if (!data)
308       return true;
309     if (!IsAligned(data)) {
310       ReportValidationError(validation_context,
311                             VALIDATION_ERROR_MISALIGNED_OBJECT);
312       return false;
313     }
314     if (!validation_context->IsValidRange(data, sizeof(ArrayHeader))) {
315       ReportValidationError(validation_context,
316                             VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
317       return false;
318     }
319     const ArrayHeader* header = static_cast<const ArrayHeader*>(data);
320     if (header->num_elements > Traits::kMaxNumElements ||
321         header->num_bytes < Traits::GetStorageSize(header->num_elements)) {
322       ReportValidationError(validation_context,
323                             VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
324       return false;
325     }
326     if (validate_params->expected_num_elements != 0 &&
327         header->num_elements != validate_params->expected_num_elements) {
328       ReportValidationError(
329           validation_context,
330           VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
331           MakeMessageWithExpectedArraySize(
332               "fixed-size array has wrong number of elements",
333               header->num_elements,
334               validate_params->expected_num_elements).c_str());
335       return false;
336     }
337     if (!validation_context->ClaimMemory(data, header->num_bytes)) {
338       ReportValidationError(validation_context,
339                             VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
340       return false;
341     }
342 
343     const Array_Data<T>* object = static_cast<const Array_Data<T>*>(data);
344     return Helper::ValidateElements(&object->header_, object->storage(),
345                                     validation_context, validate_params);
346   }
347 
348   size_t size() const { return header_.num_elements; }
349 
350   Ref at(size_t offset) {
351     DCHECK(offset < static_cast<size_t>(header_.num_elements));
352     return Traits::ToRef(storage(), offset);
353   }
354 
355   ConstRef at(size_t offset) const {
356     DCHECK(offset < static_cast<size_t>(header_.num_elements));
357     return Traits::ToConstRef(storage(), offset);
358   }
359 
360   StorageType* storage() {
361     return reinterpret_cast<StorageType*>(reinterpret_cast<char*>(this) +
362                                           sizeof(*this));
363   }
364 
365   const StorageType* storage() const {
366     return reinterpret_cast<const StorageType*>(
367         reinterpret_cast<const char*>(this) + sizeof(*this));
368   }
369 
370  private:
371   Array_Data(uint32_t num_bytes, uint32_t num_elements) {
372     header_.num_bytes = num_bytes;
373     header_.num_elements = num_elements;
374   }
375   ~Array_Data() = delete;
376 
377   internal::ArrayHeader header_;
378 
379   // Elements of type internal::ArrayDataTraits<T>::StorageType follow.
380 };
381 static_assert(sizeof(Array_Data<char>) == 8, "Bad sizeof(Array_Data)");
382 
383 // UTF-8 encoded
384 using String_Data = Array_Data<char>;
385 
386 }  // namespace internal
387 }  // namespace mojo
388 
389 #endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
390