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