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