diff options
Diffstat (limited to '')
-rw-r--r-- | mfbt/NonDereferenceable.h | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/mfbt/NonDereferenceable.h b/mfbt/NonDereferenceable.h new file mode 100644 index 0000000000..30c4cac853 --- /dev/null +++ b/mfbt/NonDereferenceable.h @@ -0,0 +1,125 @@ +/* -*- 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 <cstdint> + +// 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<T> 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 <typename T> +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<T>& 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 <typename U> + NO_POINTEE_CHECKS explicit NonDereferenceable(U* aOther) + : mPtr(static_cast<T*>(aOther)) {} + template <typename U> + NO_POINTEE_CHECKS NonDereferenceable& operator=(U* aOther) { + mPtr = static_cast<T*>(aOther); + return *this; + } + + // Construct/assign from a NonDereferenceable with a compatible pointer type. + template <typename U> + NO_POINTEE_CHECKS MOZ_IMPLICIT + NonDereferenceable(const NonDereferenceable<U>& aOther) + : mPtr(static_cast<T*>(aOther.mPtr)) {} + template <typename U> + NO_POINTEE_CHECKS NonDereferenceable& operator=( + const NonDereferenceable<U>& aOther) { + mPtr = static_cast<T*>(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<uintptr_t>(mPtr); } + + private: + // Let other NonDereferenceable templates access mPtr, to permit construction/ + // assignment from compatible pointer types. + template <typename> + friend class NonDereferenceable; + + T* MOZ_NON_OWNING_REF mPtr; +}; + +} // namespace mozilla + +#undef NO_POINTEE_CHECKS + +#endif /* mozilla_NonDereferenceable_h */ |