/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_NonDereferenceable_h #define mozilla_NonDereferenceable_h /* A pointer wrapper indicating that the pointer should not be dereferenced. */ #include "mozilla/Attributes.h" #include // Macro indicating that a function manipulates a pointer that will not be // dereferenced, and therefore there is no need to check the object. #if defined(__clang__) # define NO_POINTEE_CHECKS __attribute__((no_sanitize("vptr"))) #else # define NO_POINTEE_CHECKS /* nothing */ #endif namespace mozilla { // NonDereferenceable wraps a raw pointer value of type T*, but prevents // dereferencing. // // The main use case is for pointers that referencing memory that may not // contain a valid object, either because the object has already been freed, or // is under active construction or destruction (and hence parts of it may be // uninitialized or destructed.) // Such a pointer may still be useful, e.g., for its numeric value for // logging/debugging purposes, which may be accessed with `value()`. // Using NonDereferenceable with such pointers will make this intent clearer, // and prevent misuses. // // Note that NonDereferenceable is only a wrapper and is NOT an owning pointer, // i.e., it will not release/free the object. // // NonDereferenceable allows conversions between compatible pointer types, e.g., // to navigate a class hierarchy and identify parent/sub-objects. Note that the // converted pointers stay safely NonDereferenceable. // // Use of NonDereferenceable is required to avoid errors from sanitization tools // like `clang++ -fsanitize=vptr`, and should prevent false positives while // pointers are manipulated within NonDereferenceable objects. // template class NonDereferenceable { public: // Default construction with a null value. NonDereferenceable() : mPtr(nullptr) {} // Default copy construction and assignment. NO_POINTEE_CHECKS NonDereferenceable(const NonDereferenceable&) = default; NO_POINTEE_CHECKS NonDereferenceable& operator=(const NonDereferenceable&) = default; // No move operations, as we're only carrying a non-owning pointer, so // copying is most efficient. // Construct/assign from a T* raw pointer. // A raw pointer should usually point at a valid object, however we want to // leave the ability to the user to create a NonDereferenceable from any // pointer. Also, strictly speaking, in a constructor or destructor, `this` // points at an object still being constructed or already partially // destructed, which some very sensitive sanitizers could complain about. NO_POINTEE_CHECKS explicit NonDereferenceable(T* aPtr) : mPtr(aPtr) {} NO_POINTEE_CHECKS NonDereferenceable& operator=(T* aPtr) { mPtr = aPtr; return *this; } // Construct/assign from a compatible pointer type. template NO_POINTEE_CHECKS explicit NonDereferenceable(U* aOther) : mPtr(static_cast(aOther)) {} template NO_POINTEE_CHECKS NonDereferenceable& operator=(U* aOther) { mPtr = static_cast(aOther); return *this; } // Construct/assign from a NonDereferenceable with a compatible pointer type. template NO_POINTEE_CHECKS MOZ_IMPLICIT NonDereferenceable(const NonDereferenceable& aOther) : mPtr(static_cast(aOther.mPtr)) {} template NO_POINTEE_CHECKS NonDereferenceable& operator=( const NonDereferenceable& aOther) { mPtr = static_cast(aOther.mPtr); return *this; } // Explicitly disallow dereference operators, so that compiler errors point // at these lines: T& operator*() = delete; // Cannot dereference NonDereferenceable! T* operator->() = delete; // Cannot dereference NonDereferenceable! // Null check. NO_POINTEE_CHECKS explicit operator bool() const { return !!mPtr; } // Extract the pointer value, untyped. NO_POINTEE_CHECKS uintptr_t value() const { return reinterpret_cast(mPtr); } private: // Let other NonDereferenceable templates access mPtr, to permit construction/ // assignment from compatible pointer types. template friend class NonDereferenceable; T* MOZ_NON_OWNING_REF mPtr; }; } // namespace mozilla #undef NO_POINTEE_CHECKS #endif /* mozilla_NonDereferenceable_h */