NonDereferenceable.h (4630B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_NonDereferenceable_h 8 #define mozilla_NonDereferenceable_h 9 10 /* A pointer wrapper indicating that the pointer should not be dereferenced. */ 11 12 #include "mozilla/Attributes.h" 13 14 #include <cstdint> 15 16 // Macro indicating that a function manipulates a pointer that will not be 17 // dereferenced, and therefore there is no need to check the object. 18 #if defined(__clang__) 19 # define NO_POINTEE_CHECKS __attribute__((no_sanitize("vptr"))) 20 #else 21 # define NO_POINTEE_CHECKS /* nothing */ 22 #endif 23 24 namespace mozilla { 25 26 // NonDereferenceable<T> wraps a raw pointer value of type T*, but prevents 27 // dereferencing. 28 // 29 // The main use case is for pointers that referencing memory that may not 30 // contain a valid object, either because the object has already been freed, or 31 // is under active construction or destruction (and hence parts of it may be 32 // uninitialized or destructed.) 33 // Such a pointer may still be useful, e.g., for its numeric value for 34 // logging/debugging purposes, which may be accessed with `value()`. 35 // Using NonDereferenceable with such pointers will make this intent clearer, 36 // and prevent misuses. 37 // 38 // Note that NonDereferenceable is only a wrapper and is NOT an owning pointer, 39 // i.e., it will not release/free the object. 40 // 41 // NonDereferenceable allows conversions between compatible pointer types, e.g., 42 // to navigate a class hierarchy and identify parent/sub-objects. Note that the 43 // converted pointers stay safely NonDereferenceable. 44 // 45 // Use of NonDereferenceable is required to avoid errors from sanitization tools 46 // like `clang++ -fsanitize=vptr`, and should prevent false positives while 47 // pointers are manipulated within NonDereferenceable objects. 48 // 49 template <typename T> 50 class NonDereferenceable { 51 public: 52 // Default construction with a null value. 53 NonDereferenceable() : mPtr(nullptr) {} 54 55 // Default copy construction and assignment. 56 NO_POINTEE_CHECKS 57 NonDereferenceable(const NonDereferenceable&) = default; 58 NO_POINTEE_CHECKS 59 NonDereferenceable<T>& operator=(const NonDereferenceable&) = default; 60 // No move operations, as we're only carrying a non-owning pointer, so 61 // copying is most efficient. 62 63 // Construct/assign from a T* raw pointer. 64 // A raw pointer should usually point at a valid object, however we want to 65 // leave the ability to the user to create a NonDereferenceable from any 66 // pointer. Also, strictly speaking, in a constructor or destructor, `this` 67 // points at an object still being constructed or already partially 68 // destructed, which some very sensitive sanitizers could complain about. 69 NO_POINTEE_CHECKS 70 explicit NonDereferenceable(T* aPtr) : mPtr(aPtr) {} 71 NO_POINTEE_CHECKS 72 NonDereferenceable& operator=(T* aPtr) { 73 mPtr = aPtr; 74 return *this; 75 } 76 77 // Construct/assign from a compatible pointer type. 78 template <typename U> 79 NO_POINTEE_CHECKS explicit NonDereferenceable(U* aOther) 80 : mPtr(static_cast<T*>(aOther)) {} 81 template <typename U> 82 NO_POINTEE_CHECKS NonDereferenceable& operator=(U* aOther) { 83 mPtr = static_cast<T*>(aOther); 84 return *this; 85 } 86 87 // Construct/assign from a NonDereferenceable with a compatible pointer type. 88 template <typename U> 89 NO_POINTEE_CHECKS MOZ_IMPLICIT 90 NonDereferenceable(const NonDereferenceable<U>& aOther) 91 : mPtr(static_cast<T*>(aOther.mPtr)) {} 92 template <typename U> 93 NO_POINTEE_CHECKS NonDereferenceable& operator=( 94 const NonDereferenceable<U>& aOther) { 95 mPtr = static_cast<T*>(aOther.mPtr); 96 return *this; 97 } 98 99 // Explicitly disallow dereference operators, so that compiler errors point 100 // at these lines: 101 T& operator*() = delete; // Cannot dereference NonDereferenceable! 102 T* operator->() = delete; // Cannot dereference NonDereferenceable! 103 104 // Null check. 105 NO_POINTEE_CHECKS 106 explicit operator bool() const { return !!mPtr; } 107 108 // Extract the pointer value, untyped. 109 NO_POINTEE_CHECKS 110 uintptr_t value() const { return reinterpret_cast<uintptr_t>(mPtr); } 111 112 private: 113 // Let other NonDereferenceable templates access mPtr, to permit construction/ 114 // assignment from compatible pointer types. 115 template <typename> 116 friend class NonDereferenceable; 117 118 T* MOZ_NON_OWNING_REF mPtr; 119 }; 120 121 } // namespace mozilla 122 123 #undef NO_POINTEE_CHECKS 124 125 #endif /* mozilla_NonDereferenceable_h */