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