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