Skip to content

Commit 569e7e9

Browse files
Merge pull request ClickHouse#100281 from ClickHouse/workaround-asan-fiber-false-positive
Unpoison fiber stack before each resume to fix ASan false positives
2 parents 383d3d6 + 1f870bb commit 569e7e9

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

src/Common/AsyncTaskExecutor.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ void AsyncTaskExecutor::resume()
4242

4343
void AsyncTaskExecutor::resumeUnlocked()
4444
{
45+
/// Clear stale use-after-scope poisoning on the fiber stack before switching to it.
46+
/// See FiberStack::beforeResume() for details.
47+
fiber_stack.beforeResume();
4548
fiber.resume();
4649
}
4750

src/Common/FiberStack.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ FiberStack::FiberStack(size_t stack_size_)
4343
{
4444
}
4545

46-
boost::context::stack_context FiberStack::allocate() const
46+
boost::context::stack_context FiberStack::allocate()
4747
{
4848
size_t num_pages = 1 + (stack_size - 1) / page_size;
4949

@@ -72,6 +72,10 @@ boost::context::stack_context FiberStack::allocate() const
7272
}
7373
}
7474

75+
/// Remember the allocation for ASan unpoisoning in beforeResume().
76+
stack_base = data;
77+
stack_allocation_size = num_bytes;
78+
7579
boost::context::stack_context sctx;
7680
sctx.size = num_bytes;
7781
sctx.sp = static_cast< char * >(data) + sctx.size;

src/Common/FiberStack.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#pragma once
22
#include <boost/context/stack_context.hpp>
3+
#include <base/sanitizer_defs.h>
4+
#include <cstddef>
5+
6+
#if __has_include(<sanitizer/asan_interface.h>) && defined(ADDRESS_SANITIZER)
7+
# include <sanitizer/asan_interface.h>
8+
#endif
39

410
/// This is an implementation of allocator for fiber stack.
511
/// The reference implementation is protected_fixedsize_stack from boost::context.
@@ -16,10 +22,31 @@ class FiberStack
1622

1723
explicit FiberStack(size_t stack_size_ = default_stack_size);
1824

19-
boost::context::stack_context allocate() const;
25+
boost::context::stack_context allocate();
2026
void deallocate(boost::context::stack_context & sctx) const;
2127

28+
/// Unpoison the fiber stack memory for ASan.
29+
///
30+
/// ASan's use-after-scope detection poisons stack memory when local variables
31+
/// go out of scope. On fiber stacks, this poisoning persists across context
32+
/// switches (yield/resume), causing false positives when the same stack addresses
33+
/// are reused by new frames in subsequent fiber resumes.
34+
///
35+
/// This must be called before each fiber resume to clear stale scope poisoning.
36+
/// See https://github.com/google/sanitizers/issues/189
37+
void beforeResume() const
38+
{
39+
#if defined(ADDRESS_SANITIZER)
40+
if (stack_base)
41+
ASAN_UNPOISON_MEMORY_REGION(stack_base, stack_allocation_size);
42+
#endif
43+
}
44+
2245
private:
2346
const size_t stack_size;
2447
const size_t page_size;
48+
49+
/// Tracked allocation for ASan unpoisoning.
50+
void * stack_base = nullptr;
51+
size_t stack_allocation_size = 0;
2552
};

0 commit comments

Comments
 (0)