xref: /aosp_15_r20/external/cronet/base/memory/ref_counted.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2011 The Chromium 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 #include "base/memory/ref_counted.h"
6 
7 #include <limits>
8 #include <ostream>
9 #include <type_traits>
10 
11 #include "base/threading/thread_collision_warner.h"
12 
13 namespace base {
14 namespace {
15 
16 #if DCHECK_IS_ON()
17 std::atomic_int g_cross_thread_ref_count_access_allow_count(0);
18 #endif
19 
20 }  // namespace
21 
22 namespace subtle {
23 
HasOneRef() const24 bool RefCountedThreadSafeBase::HasOneRef() const {
25   return ref_count_.IsOne();
26 }
27 
HasAtLeastOneRef() const28 bool RefCountedThreadSafeBase::HasAtLeastOneRef() const {
29   return !ref_count_.IsZero();
30 }
31 
32 #if DCHECK_IS_ON()
~RefCountedThreadSafeBase()33 RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
34   DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without "
35                       "calling Release()";
36 }
37 #endif
38 
39 // For security and correctness, we check the arithmetic on ref counts.
40 //
41 // In an attempt to avoid binary bloat (from inlining the `CHECK`), we define
42 // these functions out-of-line. However, compilers are wily. Further testing may
43 // show that `NOINLINE` helps or hurts.
44 //
45 #if defined(ARCH_CPU_64_BITS)
AddRefImpl() const46 void RefCountedBase::AddRefImpl() const {
47   // An attacker could induce use-after-free bugs, and potentially exploit them,
48   // by creating so many references to a ref-counted object that the reference
49   // count overflows. On 32-bit architectures, there is not enough address space
50   // to succeed. But on 64-bit architectures, it might indeed be possible.
51   // Therefore, we can elide the check for arithmetic overflow on 32-bit, but we
52   // must check on 64-bit.
53   //
54   // Make sure the addition didn't wrap back around to 0. This form of check
55   // works because we assert that `ref_count_` is an unsigned integer type.
56   CHECK(++ref_count_ != 0);
57 }
58 
ReleaseImpl() const59 void RefCountedBase::ReleaseImpl() const {
60   // Make sure the subtraction didn't wrap back around from 0 to the max value.
61   // That could cause memory leaks, and may induce application-semantic
62   // correctness or safety bugs. (E.g. what if we really needed that object to
63   // be destroyed at the right time?)
64   //
65   // Note that unlike with overflow, underflow could also happen on 32-bit
66   // architectures. Arguably, we should do this check on32-bit machines too.
67   CHECK(--ref_count_ != std::numeric_limits<decltype(ref_count_)>::max());
68 }
69 #endif
70 
71 #if !(defined(ARCH_CPU_X86_FAMILY) || defined(__ARM_FEATURE_ATOMICS))
Release() const72 bool RefCountedThreadSafeBase::Release() const {
73   return ReleaseImpl();
74 }
AddRef() const75 void RefCountedThreadSafeBase::AddRef() const {
76   AddRefImpl();
77 }
AddRefWithCheck() const78 void RefCountedThreadSafeBase::AddRefWithCheck() const {
79   AddRefWithCheckImpl();
80 }
81 #endif
82 
83 #if DCHECK_IS_ON()
CalledOnValidSequence() const84 bool RefCountedBase::CalledOnValidSequence() const {
85   return sequence_checker_.CalledOnValidSequence() ||
86          g_cross_thread_ref_count_access_allow_count.load() != 0;
87 }
88 #endif
89 
90 }  // namespace subtle
91 
92 #if DCHECK_IS_ON()
ScopedAllowCrossThreadRefCountAccess()93 ScopedAllowCrossThreadRefCountAccess::ScopedAllowCrossThreadRefCountAccess() {
94   ++g_cross_thread_ref_count_access_allow_count;
95 }
96 
~ScopedAllowCrossThreadRefCountAccess()97 ScopedAllowCrossThreadRefCountAccess::~ScopedAllowCrossThreadRefCountAccess() {
98   --g_cross_thread_ref_count_access_allow_count;
99 }
100 #endif
101 
102 }  // namespace base
103