1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <android-base/logging.h> 20 #include <android-base/macros.h> 21 #include <android-base/off64_t.h> 22 23 #include <atomic> 24 #include <iterator> 25 #include <memory> 26 #include <shared_mutex> 27 #include <type_traits> 28 #include <vector> 29 30 #ifdef __ANDROID__ 31 #include <linux/incrementalfs.h> 32 #endif 33 34 namespace android { 35 36 class FileMap; 37 38 namespace incfs { 39 40 // Controls whether not verifying the presence of data before de-referencing the pointer aborts 41 // program execution. 42 #define LIBINCFS_MAP_PTR_DEBUG false 43 #if LIBINCFS_MAP_PTR_DEBUG 44 #define LIBINCFS_MAP_PTR_DEBUG_CODE(x) x 45 #else 46 #define LIBINCFS_MAP_PTR_DEBUG_CODE(x) 47 #endif 48 49 template <typename T, bool Verified = false> 50 struct map_ptr; 51 52 // This class represents a memory-mapped, read-only file that may exist on an IncFs file system. 53 // 54 // Files stored on IncFs may not be fully present. This class is able to return a smart pointer 55 // (map_ptr<T>) that is able to verify whether the contents of the pointer are fully present on 56 // IncFs. 57 // 58 // This always uses MAP_SHARED. 59 class IncFsFileMap final { 60 public: 61 IncFsFileMap() noexcept; 62 IncFsFileMap(IncFsFileMap&&) noexcept; 63 IncFsFileMap& operator =(IncFsFileMap&&) noexcept; 64 ~IncFsFileMap() noexcept; 65 66 // Initializes the map. Does not take ownership of the file descriptor. 67 // Returns whether or not the file was able to be memory-mapped. 68 bool Create(int fd, off64_t offset, size_t length, const char* file_name); 69 70 // Same thing, but allows verification to be disabled when `verify` is `false`, and enabled when 71 // `verify` is true and the file resides on IncFs. 72 bool Create(int fd, off64_t offset, size_t length, const char* file_name, bool verify); 73 74 // Same thing, but allows verification to be disabled when `verify` is `false`, and enabled when 75 // `verify` is true regardless of whether the file resides on IncFs (used for benchmarks and 76 // testing). 77 bool CreateForceVerification(int fd, off64_t offset, size_t length, const char* file_name, 78 bool verify); 79 80 template <typename T = void> data()81 map_ptr<T> data() const { 82 return map_ptr<T>(verification_enabled_ ? this : nullptr, 83 reinterpret_cast<const T*>(unsafe_data())); 84 } 85 86 const void* unsafe_data() const; 87 size_t length() const; 88 off64_t offset() const; 89 const char* file_name() const; 90 91 public: 92 // Returns whether the data range is entirely present on IncFs. 93 bool Verify(const uint8_t* const& data_start, const uint8_t* const& data_end, 94 const uint8_t** prev_verified_block) const; 95 96 private: 97 DISALLOW_COPY_AND_ASSIGN(IncFsFileMap); 98 99 using bucket_t = uint8_t; 100 static constexpr size_t kBucketBits = sizeof(bucket_t) * 8U; 101 102 // File descriptor of the memory-mapped file (not owned). 103 int fd_ = -1; 104 bool verification_enabled_ = false; 105 size_t start_block_offset_ = 0; 106 const uint8_t* start_block_ptr_ = nullptr; 107 108 std::unique_ptr<android::FileMap> map_; 109 110 // Bitwise cache for storing whether a block has already been verified. This cache relies on 111 // IncFs not deleting blocks of a file that is currently memory mapped. 112 mutable std::vector<std::atomic<bucket_t>> loaded_blocks_; 113 114 template <typename, bool> 115 friend struct map_ptr; 116 }; 117 118 // Variant of map_ptr that statically guarantees that the pointed to data is fully present and 119 // reading data will not result in IncFs raising a SIGBUS. 120 template <typename T> 121 using verified_map_ptr = map_ptr<T, true>; 122 123 // Smart pointer that is able to verify whether the contents of the pointer are fully present on 124 // the file system before using the pointer. Files residing on IncFs may not be fully present. 125 // 126 // Before attempting to use the data represented by the smart pointer, the caller should always use 127 // the bool operator to verify the presence of the data. The bool operator is not thread-safe. If 128 // this pointer must be used in multiple threads concurrently, use verified_map_ptr instead. 129 // 130 // map_ptr created from raw pointers have less overhead than when created from IncFsFileMap. 131 template <typename T, bool Verified> 132 struct map_ptr final { 133 private: 134 friend class IncFsFileMap; 135 136 // To access internals of map_ptr with a different type 137 template <typename, bool> 138 friend struct map_ptr; 139 140 template <typename T1> 141 using IsVoid = typename std::enable_if_t<std::is_void<T1>::value, int>; 142 143 template <typename T1> 144 using NotVoid = typename std::enable_if_t<!std::is_void<T1>::value, int>; 145 146 template <bool V> 147 using IsVerified = typename std::enable_if_t<V, int>; 148 149 template <bool V> 150 using IsUnverified = typename std::enable_if_t<!V, int>; 151 152 public: 153 class const_iterator final { 154 public: 155 friend struct map_ptr<T, Verified>; 156 using iterator_category = std::random_access_iterator_tag; 157 using value_type = const map_ptr<T>; 158 using difference_type = std::ptrdiff_t; 159 using pointer = void; 160 using reference = value_type; 161 162 const_iterator() = default; 163 const_iterator(const const_iterator& it) = default; 164 165 bool operator==(const const_iterator& other) const { return safe_ptr_ == other.safe_ptr_; } 166 bool operator!=(const const_iterator& other) const { return safe_ptr_ != other.safe_ptr_; } 167 std::ptrdiff_t operator-(const const_iterator& other) const { 168 return safe_ptr_ - other.safe_ptr_; 169 } 170 171 const_iterator operator+(int n) const { 172 const_iterator other = *this; 173 other += n; 174 return other; 175 } 176 177 reference operator*() const { return safe_ptr_; } 178 179 const const_iterator& operator++() { 180 safe_ptr_++; 181 return *this; 182 } 183 184 const_iterator& operator+=(int n) { 185 safe_ptr_ = safe_ptr_ + n; 186 return *this; 187 } 188 189 const const_iterator operator++(int) { 190 const_iterator temp(*this); 191 safe_ptr_++; 192 return temp; 193 } 194 195 private: 196 explicit const_iterator(const map_ptr<T>& ptr) : safe_ptr_(ptr) {} 197 map_ptr<T> safe_ptr_; 198 }; 199 200 // Default constructor 201 map_ptr() = default; 202 203 // Implicit conversion from raw pointer 204 map_ptr(const T* ptr) : map_ptr(nullptr, ptr, nullptr) {} 205 206 // Copy constructor 207 map_ptr(const map_ptr& other) = default; 208 209 // Implicit copy conversion from verified to unverified map_ptr<T> 210 template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0> 211 map_ptr(const map_ptr<T, V2>& other) : map_ptr(other.map_, other.ptr_, other.verified_block_) {} 212 213 // Move constructor 214 map_ptr(map_ptr&& other) noexcept = default; 215 216 // Implicit move conversion from verified to unverified map_ptr<T> 217 template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0> 218 map_ptr(map_ptr&& other) : map_ptr(other.map_, other.ptr_, other.verified_block_) {} 219 220 // Implicit conversion to unverified map_ptr<void> 221 template <typename U, bool V2, typename T1 = T, bool V1 = Verified, IsVoid<T1> = 0, 222 NotVoid<U> = 0, IsUnverified<V1> = 0> 223 map_ptr(const map_ptr<U, V2>& other) 224 : map_ptr(other.map_, reinterpret_cast<const void*>(other.ptr_), other.verified_block_) {} 225 226 // Implicit conversion from regular raw pointer 227 map_ptr& operator=(const T* ptr) { 228 ptr_ = ptr; 229 map_ = nullptr; 230 verified_block_ = nullptr; 231 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = Verified); 232 return *this; 233 } 234 235 // Copy assignment operator 236 map_ptr& operator=(const map_ptr& other) = default; 237 238 // Copy assignment operator 239 template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0> 240 map_ptr& operator=(const map_ptr<T, V2>& other) { 241 ptr_ = other.ptr_; 242 map_ = other.map_; 243 verified_block_ = other.verified_block_; 244 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = other.verified_); 245 return *this; 246 } 247 248 template <bool V2> 249 bool operator==(const map_ptr<T, V2>& other) const { 250 return ptr_ == other.ptr_; 251 } 252 253 template <bool V2> 254 bool operator!=(const map_ptr<T, V2>& other) const { 255 return ptr_ != other.ptr_; 256 } 257 258 template <bool V2> 259 bool operator<(const map_ptr<T, V2>& other) const { 260 return ptr_ < other.ptr_; 261 } 262 263 template <bool V2> 264 std::ptrdiff_t operator-(const map_ptr<T, V2>& other) const { 265 return ptr_ - other.ptr_; 266 } 267 268 template <typename U> 269 map_ptr<U> convert() const { 270 return map_ptr<U>(map_, reinterpret_cast<const U*>(ptr_), verified_block_); 271 } 272 273 // Retrieves a map_ptr<T> offset from an original map_ptr<U> by the specified number of `offset` 274 // bytes. 275 map_ptr<T> offset(std::ptrdiff_t offset) const { 276 return map_ptr<T>(map_, 277 reinterpret_cast<const T*>(reinterpret_cast<const uint8_t*>(ptr_) + 278 offset), 279 verified_block_); 280 } 281 282 // Returns a raw pointer to the value of this pointer. 283 const T* unsafe_ptr() const { return ptr_; } 284 285 // Start T == void methods 286 287 template <typename T1 = T, IsVoid<T1> = 0> 288 operator bool() const { 289 return ptr_ != nullptr; 290 } 291 292 // End T == void methods 293 // Start T != void methods 294 295 template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsUnverified<V1> = 0> 296 operator bool() const { 297 return verify(); 298 } 299 300 template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsVerified<V1> = 0> 301 operator bool() const { 302 return ptr_ != nullptr; 303 } 304 305 template <typename T1 = T, NotVoid<T1> = 0> 306 const_iterator iterator() const { 307 return const_iterator(*this); 308 } 309 310 template <typename T1 = T, NotVoid<T1> = 0> 311 const map_ptr<T1>& operator++() { 312 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false); 313 ++ptr_; 314 return *this; 315 } 316 317 template <typename T1 = T, NotVoid<T1> = 0> 318 const map_ptr<T1> operator++(int) { 319 map_ptr<T1> temp = *this; 320 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false); 321 ++ptr_; 322 return temp; 323 } 324 325 template <typename S, typename T1 = T, NotVoid<T1> = 0> 326 map_ptr<T1> operator+(const S n) const { 327 return map_ptr<T1>(map_, ptr_ + n, verified_block_); 328 } 329 330 template <typename S, typename T1 = T, NotVoid<T1> = 0> 331 map_ptr<T1> operator-(const S n) const { 332 return map_ptr<T1>(map_, ptr_ - n, verified_block_); 333 } 334 335 // Returns the value of the pointer. 336 // The caller should verify the presence of the pointer data before calling this method. 337 template <typename T1 = T, NotVoid<T1> = 0> 338 const T1& value() const { 339 LIBINCFS_MAP_PTR_DEBUG_CODE( 340 CHECK(verified_) << "Did not verify presence before de-referencing safe pointer"); 341 return *ptr_; 342 } 343 344 // Returns a raw pointer to the value this pointer. 345 // The caller should verify the presence of the pointer data before calling this method. 346 template <typename T1 = T, NotVoid<T1> = 0> 347 const T1* operator->() const { 348 LIBINCFS_MAP_PTR_DEBUG_CODE( 349 CHECK(verified_) << "Did not verify presence before de-referencing safe pointer"); 350 return ptr_; 351 } 352 353 // Verifies the presence of `n` elements of `T`. 354 // 355 // Returns true if the elements are completely present; otherwise, returns false. 356 template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsUnverified<V1> = 0> 357 bool verify(size_t n = 1) const { 358 if (ptr_ == nullptr) { 359 return false; 360 } 361 362 #ifdef __ANDROID__ 363 if (LIKELY(map_ == nullptr)) { 364 return ptr_ != nullptr; 365 } 366 367 const size_t verify_size = sizeof(T) * n; 368 LIBINCFS_MAP_PTR_DEBUG_CODE(if (sizeof(T) <= verify_size) verified_ = true;); 369 370 const auto data_start = reinterpret_cast<const uint8_t*>(ptr_); 371 const auto data_end = reinterpret_cast<const uint8_t*>(ptr_) + verify_size; 372 373 // If the data is entirely within the block beginning at the previous verified block 374 // pointer, then the data can safely be used. 375 if (LIKELY(data_start >= verified_block_ && 376 data_end <= verified_block_ + INCFS_DATA_FILE_BLOCK_SIZE)) { 377 return true; 378 } 379 380 if (LIKELY(map_->Verify(data_start, data_end, &verified_block_))) { 381 return true; 382 } 383 384 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false); 385 return false; 386 #else 387 (void)n; 388 return true; 389 #endif 390 } 391 392 // Returns a verified version of this pointer. 393 // The caller should verify the presence of the pointer data before calling this method. 394 template <typename T1 = T, NotVoid<T1> = 0> 395 verified_map_ptr<T1> verified() const { 396 return verified_map_ptr<T1>(map_, ptr_, verified_block_); 397 } 398 399 private: 400 map_ptr(const IncFsFileMap* map, const T* ptr) 401 : ptr_(ptr), map_(map), verified_block_(nullptr) {} 402 map_ptr(const IncFsFileMap* map, const T* ptr, const uint8_t* verified_block) 403 : ptr_(ptr), map_(map), verified_block_(verified_block) {} 404 405 const T* ptr_ = nullptr; 406 mutable const IncFsFileMap* map_ = nullptr; 407 mutable const uint8_t* verified_block_; 408 LIBINCFS_MAP_PTR_DEBUG_CODE(mutable bool verified_ = Verified); 409 }; 410 411 } // namespace incfs 412 413 } // namespace android