Skip to content

Commit c80e657

Browse files
bugaevcawesomekling
authored andcommitted
AK: Switch RefCounted to atomic refcounting
This fixes all sorts of race conditions, primarily in the kernel, where till now it's been possible to obtain either double free or use-after-free by exploiting refcounting races.
1 parent 5831080 commit c80e657

File tree

1 file changed

+11
-9
lines changed

1 file changed

+11
-9
lines changed

AK/RefCounted.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#pragma once
2828

2929
#include <AK/Assertions.h>
30+
#include <AK/Atomic.h>
3031
#include <AK/Platform.h>
3132
#include <AK/StdLibExtras.h>
3233

@@ -62,8 +63,8 @@ class RefCountedBase {
6263

6364
ALWAYS_INLINE void ref() const
6465
{
65-
ASSERT(m_ref_count);
66-
++m_ref_count;
66+
auto old_ref_count = m_ref_count++;
67+
ASSERT(old_ref_count > 0);
6768
}
6869

6970
ALWAYS_INLINE RefCountType ref_count() const
@@ -78,25 +79,26 @@ class RefCountedBase {
7879
ASSERT(m_ref_count == 0);
7980
}
8081

81-
ALWAYS_INLINE void deref_base() const
82+
ALWAYS_INLINE RefCountType deref_base() const
8283
{
83-
ASSERT(m_ref_count);
84-
--m_ref_count;
84+
auto old_ref_count = m_ref_count--;
85+
ASSERT(old_ref_count > 0);
86+
return old_ref_count - 1;
8587
}
8688

87-
mutable RefCountType m_ref_count { 1 };
89+
mutable Atomic<RefCountType> m_ref_count { 1 };
8890
};
8991

9092
template<typename T>
9193
class RefCounted : public RefCountedBase {
9294
public:
9395
void unref() const
9496
{
95-
deref_base();
96-
if (m_ref_count == 0) {
97+
auto new_ref_count = deref_base();
98+
if (new_ref_count == 0) {
9799
call_will_be_destroyed_if_present(static_cast<const T*>(this));
98100
delete static_cast<const T*>(this);
99-
} else if (m_ref_count == 1) {
101+
} else if (new_ref_count == 1) {
100102
call_one_ref_left_if_present(static_cast<const T*>(this));
101103
}
102104
}

0 commit comments

Comments
 (0)