summaryrefslogtreecommitdiffstats
path: root/mfbt/NonDereferenceable.h
blob: 30c4cac853e9cf83da37231f99c2fe80205db39f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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 */