1 // Copyright 2017 The PDFium Authors
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 CORE_FXCRT_UNOWNED_PTR_H_
6 #define CORE_FXCRT_UNOWNED_PTR_H_
7
8 // UnownedPtr is a smart pointer class that behaves very much like a
9 // standard C-style pointer. The advantages of using it over native T*
10 // pointers are:
11 //
12 // 1. It documents the nature of the pointer with no need to add a comment
13 // explaining that is it // Not owned.
14 //
15 // 2. An attempt to delete an unowned ptr will fail to compile rather
16 // than silently succeeding, since it is a class and not a raw pointer.
17 //
18 // 3. When built using the memory tool ASAN, the class provides a destructor
19 // which checks that the object being pointed to is still alive.
20 //
21 // 4. When built against PartitionAlloc's BRP feature, it provides the same
22 // UaF protections as base::raw_ptr<T>
23 //
24 // 5. It is initialized to nullptr by default.
25 //
26 // Hence, when using UnownedPtr, no dangling pointers are ever permitted,
27 // even if they are not de-referenced after becoming dangling. The style of
28 // programming required is that the lifetime an object containing an
29 // UnownedPtr must be strictly less than the object to which it points.
30 //
31 // The same checks are also performed at assignment time to prove that the
32 // old value was not a dangling pointer, either.
33 //
34 // The array indexing operation [] is not supported on an unowned ptr,
35 // because an unowned ptr expresses a one to one relationship with some
36 // other heap object. Use pdfium::span<> for the cases where indexing
37 // into an unowned array is desired, which performs the same checks.
38
39 #include "build/build_config.h"
40
41 #if defined(PDF_USE_PARTITION_ALLOC)
42 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
43
44 // Can only use base::raw_ptr<> impls that force nullptr initialization.
45 #if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || BUILDFLAG(USE_ASAN_UNOWNED_PTR)
46 #define UNOWNED_PTR_IS_BASE_RAW_PTR
47 #endif
48
49 #if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) || BUILDFLAG(USE_ASAN_UNOWNED_PTR)
50 #define UNOWNED_PTR_DANGLING_CHECKS
51 #endif
52 #endif // PDF_USE_PARTITION_ALLOC
53
54 #if defined(UNOWNED_PTR_IS_BASE_RAW_PTR)
55 #include "base/allocator/partition_allocator/pointers/raw_ptr.h"
56
57 template <typename T>
58 using UnownedPtr = raw_ptr<T>;
59
60 #else // UNOWNED_PTR_IS_BASE_RAW_PTR
61
62 #include <cstddef>
63 #include <functional>
64 #include <type_traits>
65 #include <utility>
66
67 #include "core/fxcrt/unowned_ptr_exclusion.h"
68 #include "third_party/base/compiler_specific.h"
69
70 #if defined(ADDRESS_SANITIZER)
71 #include <cstdint>
72 #define UNOWNED_PTR_DANGLING_CHECKS
73 #endif
74
75 namespace pdfium {
76
77 template <typename T>
78 class span;
79
80 } // namespace pdfium
81
82 namespace fxcrt {
83
84 template <class T>
85 class TRIVIAL_ABI GSL_POINTER UnownedPtr {
86 public:
87 constexpr UnownedPtr() noexcept = default;
88
89 // Deliberately implicit to allow returning nullptrs.
90 // NOLINTNEXTLINE(runtime/explicit)
UnownedPtr(std::nullptr_t ptr)91 constexpr UnownedPtr(std::nullptr_t ptr) {}
92
UnownedPtr(T * pObj)93 explicit constexpr UnownedPtr(T* pObj) noexcept : m_pObj(pObj) {}
94
95 // Copy-construct an UnownedPtr.
96 // Required in addition to copy conversion constructor below.
UnownedPtr(const UnownedPtr & that)97 constexpr UnownedPtr(const UnownedPtr& that) noexcept
98 : m_pObj(static_cast<T*>(that)) {}
99
100 // Move-construct an UnownedPtr. After construction, |that| will be NULL.
101 // Required in addition to move conversion constructor below.
UnownedPtr(UnownedPtr && that)102 constexpr UnownedPtr(UnownedPtr&& that) noexcept
103 : m_pObj(that.ExtractAsDangling()) {}
104
105 // Copy-conversion constructor.
106 template <class U,
107 typename = typename std::enable_if<
108 std::is_convertible<U*, T*>::value>::type>
UnownedPtr(const UnownedPtr<U> & that)109 UnownedPtr(const UnownedPtr<U>& that) : UnownedPtr(static_cast<U*>(that)) {}
110
111 // Move-conversion constructor.
112 template <class U,
113 typename = typename std::enable_if<
114 std::is_convertible<U*, T*>::value>::type>
UnownedPtr(UnownedPtr<U> && that)115 UnownedPtr(UnownedPtr<U>&& that) noexcept {
116 Reset(that.ExtractAsDangling());
117 }
118
119 // Assign an UnownedPtr from nullptr.
120 UnownedPtr& operator=(std::nullptr_t) noexcept {
121 Reset();
122 return *this;
123 }
124
125 // Assign an UnownedPtr from a raw ptr.
126 UnownedPtr& operator=(T* that) noexcept {
127 Reset(that);
128 return *this;
129 }
130
131 // Copy-assign an UnownedPtr.
132 // Required in addition to copy conversion assignment below.
133 UnownedPtr& operator=(const UnownedPtr& that) noexcept {
134 if (*this != that)
135 Reset(static_cast<T*>(that));
136 return *this;
137 }
138
139 // Move-assign an UnownedPtr. After assignment, |that| will be NULL.
140 // Required in addition to move conversion assignment below.
141 UnownedPtr& operator=(UnownedPtr&& that) noexcept {
142 if (*this != that)
143 Reset(that.ExtractAsDangling());
144 return *this;
145 }
146
147 // Copy-convert assignment.
148 template <class U,
149 typename = typename std::enable_if<
150 std::is_convertible<U*, T*>::value>::type>
151 UnownedPtr& operator=(const UnownedPtr<U>& that) noexcept {
152 if (*this != that)
153 Reset(that);
154 return *this;
155 }
156
157 // Move-convert assignment. After assignment, |that| will be NULL.
158 template <class U,
159 typename = typename std::enable_if<
160 std::is_convertible<U*, T*>::value>::type>
161 UnownedPtr& operator=(UnownedPtr<U>&& that) noexcept {
162 if (*this != that)
163 Reset(that.ExtractAsDangling());
164 return *this;
165 }
166
~UnownedPtr()167 ~UnownedPtr() {
168 ProbeForLowSeverityLifetimeIssue();
169 m_pObj = nullptr;
170 }
171
172 bool operator==(std::nullptr_t ptr) const { return m_pObj == nullptr; }
173 bool operator==(const UnownedPtr& that) const {
174 return m_pObj == static_cast<T*>(that);
175 }
176 bool operator<(const UnownedPtr& that) const {
177 return std::less<T*>()(m_pObj, static_cast<T*>(that));
178 }
179
180 operator T*() const noexcept { return m_pObj; }
get()181 T* get() const noexcept { return m_pObj; }
182
ExtractAsDangling()183 T* ExtractAsDangling() {
184 ProbeForLowSeverityLifetimeIssue();
185 T* pTemp = nullptr;
186 std::swap(pTemp, m_pObj);
187 return pTemp;
188 }
189
190 explicit operator bool() const { return !!m_pObj; }
191 T& operator*() const { return *m_pObj; }
192 T* operator->() const { return m_pObj; }
193
194 private:
195 friend class pdfium::span<T>;
196
197 void Reset(T* obj = nullptr) {
198 ProbeForLowSeverityLifetimeIssue();
199 m_pObj = obj;
200 }
201
ProbeForLowSeverityLifetimeIssue()202 inline void ProbeForLowSeverityLifetimeIssue() {
203 #if defined(ADDRESS_SANITIZER)
204 if (m_pObj)
205 reinterpret_cast<const volatile uint8_t*>(m_pObj)[0];
206 #endif
207 }
208
ReleaseBadPointer()209 inline void ReleaseBadPointer() {
210 #if defined(ADDRESS_SANITIZER)
211 m_pObj = nullptr;
212 #endif
213 }
214
215 UNOWNED_PTR_EXCLUSION T* m_pObj = nullptr;
216 };
217
218 } // namespace fxcrt
219
220 using fxcrt::UnownedPtr;
221
222 #endif // defined(UNOWNED_PTR_IS_BASE_RAW_PTR)
223
224 namespace pdfium {
225
226 // Type-deducing wrapper to make an UnownedPtr from an ordinary pointer,
227 // since equivalent constructor is explicit.
228 template <typename T>
WrapUnowned(T * that)229 UnownedPtr<T> WrapUnowned(T* that) {
230 return UnownedPtr<T>(that);
231 }
232
233 } // namespace pdfium
234
235 #endif // CORE_FXCRT_UNOWNED_PTR_H_
236