Skip to content

Commit 9038eec

Browse files
committed
memory: fix a few UAF cases, add asan to test
1 parent 51a4f93 commit 9038eec

File tree

4 files changed

+53
-27
lines changed

4 files changed

+53
-27
lines changed

CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,18 @@ if(BUILD_TESTING)
6262
file(GLOB_RECURSE TESTFILES CONFIGURE_DEPENDS "tests/*.cpp")
6363
add_executable(hyprutils_tests ${TESTFILES})
6464

65-
target_compile_options(hyprutils_tests PRIVATE --coverage)
65+
target_compile_options(hyprutils_tests PRIVATE --coverage -fsanitize=address)
6666
target_link_options(hyprutils_tests PRIVATE --coverage)
6767

6868
target_include_directories(
6969
hyprutils_tests
7070
PUBLIC "./include"
7171
PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
72-
target_link_libraries(hyprutils_tests PRIVATE hyprutils GTest::gtest_main
72+
target_link_libraries(hyprutils_tests PRIVATE asan hyprutils GTest::gtest_main
7373
PkgConfig::deps)
74-
gtest_discover_tests(hyprutils_tests)
74+
gtest_discover_tests(hyprutils_tests
75+
PROPERTIES ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0"
76+
)
7577

7678
# Add coverage to hyprutils for test builds
7779
target_compile_options(hyprutils PRIVATE --coverage)

include/hyprutils/memory/SharedPtr.hpp

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,15 @@ namespace Hyprutils {
6767
}
6868

6969
~CSharedPointer() {
70-
decrement();
70+
decrement(impl_);
7171
}
7272

7373
template <typename U>
7474
validHierarchy<const CSharedPointer<U>&> operator=(const CSharedPointer<U>& rhs) {
7575
if (impl_ == rhs.impl_)
7676
return *this;
7777

78-
decrement();
78+
decrement(impl_);
7979
impl_ = rhs.impl_;
8080
m_data = rhs.m_data;
8181
increment();
@@ -86,7 +86,7 @@ namespace Hyprutils {
8686
if (impl_ == rhs.impl_)
8787
return *this;
8888

89-
decrement();
89+
decrement(impl_);
9090
impl_ = rhs.impl_;
9191
m_data = rhs.m_data;
9292
increment();
@@ -133,9 +133,10 @@ namespace Hyprutils {
133133
}
134134

135135
void reset() {
136-
decrement();
137-
impl_ = nullptr;
138-
m_data = nullptr;
136+
auto ptr = impl_;
137+
impl_ = nullptr;
138+
m_data = nullptr;
139+
decrement(ptr);
139140
}
140141

141142
T* get() const {
@@ -161,15 +162,15 @@ namespace Hyprutils {
161162
may delete the stored object if ref == 0
162163
may delete and reset impl_ if ref == 0 and weak == 0
163164
*/
164-
void decrement() {
165-
if (!impl_)
165+
void decrement(Impl_::impl_base* base) {
166+
if (!base)
166167
return;
167168

168-
impl_->dec();
169+
base->dec();
169170

170171
// if ref == 0, we can destroy impl
171-
if (impl_->ref() == 0)
172-
destroyImpl();
172+
if (base->ref() == 0)
173+
destroyImpl(base);
173174
}
174175
/* no-op if there is no impl_ */
175176
void increment() {
@@ -181,15 +182,13 @@ namespace Hyprutils {
181182

182183
/* destroy the pointed-to object
183184
if able, will also destroy impl */
184-
void destroyImpl() {
185-
// destroy the impl contents
186-
impl_->destroy();
187-
188-
// check for weak refs, if zero, we can also delete impl_
189-
if (impl_->wref() == 0) {
190-
delete impl_;
191-
impl_ = nullptr;
192-
}
185+
void destroyImpl(Impl_::impl_base* base) {
186+
// this call can destroy this, so we need to not use thisptr anymore
187+
base->destroy();
188+
189+
// check for weak refs, if zero, we can also delete base
190+
if (base->wref() == 0)
191+
delete base;
193192
}
194193
};
195194

src/signal/Signal.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ using namespace Hyprutils::Memory;
1010
#define WP CWeakPointer
1111

1212
void Hyprutils::Signal::CSignalBase::emitInternal(void* args) {
13+
14+
// Save, an event can destroy thisptr
15+
const auto STATICS = m_vStaticListeners;
16+
1317
if (!m_vListeners.empty()) {
1418
std::vector<SP<CSignalListener>> listeners;
1519
listeners.reserve(m_vListeners.size());
@@ -34,10 +38,8 @@ void Hyprutils::Signal::CSignalBase::emitInternal(void* args) {
3438
listeners.clear();
3539
}
3640

37-
if (!m_vStaticListeners.empty()) {
38-
const auto statics = m_vStaticListeners;
39-
40-
for (const auto& l : statics) {
41+
if (!STATICS.empty()) {
42+
for (const auto& l : STATICS) {
4143
l->emitInternal(args);
4244
}
4345
}

tests/memory/Memory.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,27 @@ static void testHierarchy() {
236236
}
237237
}
238238

239+
class CSelfDestruct {
240+
public:
241+
SP<CSelfDestruct> self;
242+
243+
//
244+
void youShouldKysNOW() {
245+
self.reset();
246+
}
247+
};
248+
249+
static void testSelfDestruct() {
250+
auto x = makeShared<CSelfDestruct>();
251+
x->self = x;
252+
WP<CSelfDestruct> weak = x;
253+
x.reset();
254+
255+
// this has no EXPECT, because all we check is if there isn't a UAF here.
256+
// if there is, asan will abort us
257+
weak->youShouldKysNOW();
258+
}
259+
239260
TEST(Memory, memory) {
240261
SP<int> intPtr = makeShared<int>(10);
241262
SP<int> intPtr2 = makeShared<int>(-1337);
@@ -291,4 +312,6 @@ TEST(Memory, memory) {
291312
testAtomicImpl();
292313

293314
testHierarchy();
315+
316+
testSelfDestruct();
294317
}

0 commit comments

Comments
 (0)