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