Skip to content

Commit 7bb3e4c

Browse files
authored
Merge pull request #917 from Altinity/backports/25.3.6/79147
25.3.6 Backport of ClickHouse#79147: Remove guard pages from fibers and alternative stack for signals in threads
2 parents ad99ec2 + e8f7341 commit 7bb3e4c

File tree

13 files changed

+242
-86
lines changed

13 files changed

+242
-86
lines changed

programs/keeper/Keeper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "Keeper.h"
22

33
#include <Common/ClickHouseRevision.h>
4+
#include <Common/formatReadable.h>
45
#include <Common/getMultipleKeysFromConfig.h>
56
#include <Common/DNSResolver.h>
67
#include <Interpreters/DNSCacheUpdater.h>

src/Client/BuzzHouse/Generator/FuzzConfig.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <ranges>
44
#include <IO/copyData.h>
55
#include <Common/Exception.h>
6+
#include <Common/formatReadable.h>
67

78
namespace DB
89
{

src/Client/ClientBase.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <Core/Protocol.h>
1111
#include <Common/DateLUT.h>
1212
#include <Common/MemoryTracker.h>
13+
#include <Common/formatReadable.h>
1314
#include <Common/scope_guard_safe.h>
1415
#include <Common/Exception.h>
1516
#include <Common/ErrorCodes.h>

src/Client/Connection.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <Common/DNSResolver.h>
2424
#include <Common/StringUtils.h>
2525
#include <Common/OpenSSLHelpers.h>
26+
#include <Common/formatReadable.h>
2627
#include <Common/randomSeed.h>
2728
#include <Core/Block.h>
2829
#include <Core/ProtocolDefines.h>

src/Common/AsyncTaskExecutor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <Common/AsyncTaskExecutor.h>
22
#include <base/scope_guard.h>
3+
#include <fmt/format.h>
34

45

56
namespace DB

src/Common/AsyncTaskExecutor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#pragma once
22

3+
#include <atomic>
4+
#include <mutex>
5+
#include <base/types.h>
36
#include <Common/Epoll.h>
47
#include <Common/Fiber.h>
58
#include <Common/FiberStack.h>

src/Common/FiberStack.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#include <base/defines.h>
2+
#include <Common/formatReadable.h>
3+
#include <Common/CurrentMemoryTracker.h>
4+
#include <Common/Exception.h>
5+
#include <Common/memory.h>
6+
#include <base/getPageSize.h>
7+
#include <sys/time.h>
8+
#include <sys/resource.h>
9+
#include <sys/mman.h>
10+
#include <Common/FiberStack.h>
11+
12+
#if defined(BOOST_USE_VALGRIND)
13+
#include <valgrind/valgrind.h>
14+
#endif
15+
16+
/// Required for older Darwin builds, that lack definition of MAP_ANONYMOUS
17+
#ifndef MAP_ANONYMOUS
18+
#define MAP_ANONYMOUS MAP_ANON
19+
#endif
20+
21+
namespace
22+
{
23+
constexpr bool guardPagesEnabled()
24+
{
25+
#ifdef DEBUG_OR_SANITIZER_BUILD
26+
return true;
27+
#else
28+
return false;
29+
#endif
30+
}
31+
}
32+
33+
namespace DB::ErrorCodes
34+
{
35+
extern const int CANNOT_ALLOCATE_MEMORY;
36+
}
37+
38+
39+
FiberStack::FiberStack(size_t stack_size_)
40+
: stack_size(stack_size_)
41+
, page_size(getPageSize())
42+
{
43+
}
44+
45+
boost::context::stack_context FiberStack::allocate() const
46+
{
47+
size_t num_pages = 1 + (stack_size - 1) / page_size;
48+
49+
if constexpr (guardPagesEnabled())
50+
/// Add one page at bottom that will be used as guard-page
51+
num_pages += 1;
52+
53+
size_t num_bytes = num_pages * page_size;
54+
55+
void * data = aligned_alloc(page_size, num_bytes);
56+
57+
if (!data)
58+
throw DB::ErrnoException(DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY, "Cannot allocate FiberStack");
59+
60+
if constexpr (guardPagesEnabled())
61+
{
62+
/// TODO: make reports on illegal guard page access more clear.
63+
/// Currently we will see segfault and almost random stacktrace.
64+
try
65+
{
66+
memoryGuardInstall(data, page_size);
67+
}
68+
catch (...)
69+
{
70+
free(data);
71+
throw;
72+
}
73+
}
74+
75+
auto trace = CurrentMemoryTracker::alloc(num_bytes);
76+
trace.onAlloc(data, num_bytes);
77+
78+
boost::context::stack_context sctx;
79+
sctx.size = num_bytes;
80+
sctx.sp = static_cast< char * >(data) + sctx.size;
81+
#if defined(BOOST_USE_VALGRIND)
82+
sctx.valgrind_stack_id = VALGRIND_STACK_REGISTER(sctx.sp, data);
83+
#endif
84+
return sctx;
85+
}
86+
87+
void FiberStack::deallocate(boost::context::stack_context & sctx) const
88+
{
89+
#if defined(BOOST_USE_VALGRIND)
90+
VALGRIND_STACK_DEREGISTER(sctx.valgrind_stack_id);
91+
#endif
92+
void * data = static_cast< char * >(sctx.sp) - sctx.size;
93+
94+
if constexpr (guardPagesEnabled())
95+
memoryGuardRemove(data, page_size);
96+
97+
free(data);
98+
99+
/// Do not count guard page in memory usage.
100+
auto trace = CurrentMemoryTracker::free(sctx.size);
101+
trace.onFree(data, sctx.size - page_size);
102+
}

src/Common/FiberStack.h

Lines changed: 6 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,11 @@
11
#pragma once
2-
#include <base/defines.h>
32
#include <boost/context/stack_context.hpp>
4-
#include <Common/formatReadable.h>
5-
#include <Common/CurrentMemoryTracker.h>
6-
#include <Common/Exception.h>
7-
#include <base/getPageSize.h>
8-
#include <sys/time.h>
9-
#include <sys/resource.h>
10-
#include <sys/mman.h>
11-
12-
#if defined(BOOST_USE_VALGRIND)
13-
#include <valgrind/valgrind.h>
14-
#endif
15-
16-
/// Required for older Darwin builds, that lack definition of MAP_ANONYMOUS
17-
#ifndef MAP_ANONYMOUS
18-
#define MAP_ANONYMOUS MAP_ANON
19-
#endif
20-
21-
namespace DB::ErrorCodes
22-
{
23-
extern const int CANNOT_ALLOCATE_MEMORY;
24-
}
253

264
/// This is an implementation of allocator for fiber stack.
275
/// The reference implementation is protected_fixedsize_stack from boost::context.
286
/// This implementation additionally track memory usage. It is the main reason why it is needed.
297
class FiberStack
308
{
31-
private:
32-
size_t stack_size;
33-
size_t page_size = 0;
349
public:
3510
/// NOTE: If you see random segfaults in CI and stack starts from boost::context::...fiber...
3611
/// probably it worth to try to increase stack size for coroutines.
@@ -39,51 +14,12 @@ class FiberStack
3914
/// way. We will have 80 pages with 4KB page size.
4015
static constexpr size_t default_stack_size = 320 * 1024; /// 64KB was not enough for tests
4116

42-
explicit FiberStack(size_t stack_size_ = default_stack_size) : stack_size(stack_size_)
43-
{
44-
page_size = getPageSize();
45-
}
46-
47-
boost::context::stack_context allocate() const
48-
{
49-
size_t num_pages = 1 + (stack_size - 1) / page_size;
50-
size_t num_bytes = (num_pages + 1) * page_size; /// Add one page at bottom that will be used as guard-page
17+
explicit FiberStack(size_t stack_size_ = default_stack_size);
5118

52-
void * vp = ::mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
53-
if (MAP_FAILED == vp)
54-
throw DB::ErrnoException(DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY, "FiberStack: Cannot mmap {}.", ReadableSize(num_bytes));
19+
boost::context::stack_context allocate() const;
20+
void deallocate(boost::context::stack_context & sctx) const;
5521

56-
/// TODO: make reports on illegal guard page access more clear.
57-
/// Currently we will see segfault and almost random stacktrace.
58-
if (-1 == ::mprotect(vp, page_size, PROT_NONE))
59-
{
60-
::munmap(vp, num_bytes);
61-
throw DB::ErrnoException(DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY, "FiberStack: cannot protect guard page");
62-
}
63-
64-
/// Do not count guard page in memory usage.
65-
auto trace = CurrentMemoryTracker::alloc(num_pages * page_size);
66-
trace.onAlloc(vp, num_pages * page_size);
67-
68-
boost::context::stack_context sctx;
69-
sctx.size = num_bytes;
70-
sctx.sp = static_cast< char * >(vp) + sctx.size;
71-
#if defined(BOOST_USE_VALGRIND)
72-
sctx.valgrind_stack_id = VALGRIND_STACK_REGISTER(sctx.sp, vp);
73-
#endif
74-
return sctx;
75-
}
76-
77-
void deallocate(boost::context::stack_context & sctx) const
78-
{
79-
#if defined(BOOST_USE_VALGRIND)
80-
VALGRIND_STACK_DEREGISTER(sctx.valgrind_stack_id);
81-
#endif
82-
void * vp = static_cast< char * >(sctx.sp) - sctx.size;
83-
::munmap(vp, sctx.size);
84-
85-
/// Do not count guard page in memory usage.
86-
auto trace = CurrentMemoryTracker::free(sctx.size - page_size);
87-
trace.onFree(vp, sctx.size - page_size);
88-
}
22+
private:
23+
const size_t stack_size;
24+
const size_t page_size;
8925
};

src/Common/ThreadStatus.cpp

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <Common/CurrentThread.h>
66
#include <Common/logger_useful.h>
77
#include <Common/MemoryTrackerBlockerInThread.h>
8+
#include <Common/memory.h>
89
#include <base/getPageSize.h>
910
#include <base/errnoToString.h>
1011
#include <Interpreters/Context.h>
@@ -19,10 +20,24 @@ namespace DB
1920
{
2021
thread_local ThreadStatus constinit * current_thread = nullptr;
2122

23+
namespace ErrorCodes
24+
{
25+
extern const int CANNOT_ALLOCATE_MEMORY;
26+
}
27+
2228
#if !defined(SANITIZER)
2329
namespace
2430
{
2531

32+
constexpr bool guardPagesEnabled()
33+
{
34+
#ifdef DEBUG_OR_SANITIZER_BUILD
35+
return true;
36+
#else
37+
return false;
38+
#endif
39+
}
40+
2641
/// For aarch64 16K is not enough (likely due to tons of registers)
2742
constexpr size_t UNWIND_MINSIGSTKSZ = 32 << 10;
2843

@@ -41,24 +56,48 @@ constexpr size_t UNWIND_MINSIGSTKSZ = 32 << 10;
4156
struct ThreadStack
4257
{
4358
ThreadStack()
44-
: data(aligned_alloc(getPageSize(), getSize()))
4559
{
46-
/// Add a guard page
47-
/// (and since the stack grows downward, we need to protect the first page).
48-
mprotect(data, getPageSize(), PROT_NONE);
60+
auto page_size = getPageSize();
61+
data = aligned_alloc(page_size, getSize());
62+
if (!data)
63+
throw ErrnoException(ErrorCodes::CANNOT_ALLOCATE_MEMORY, "Cannot allocate ThreadStack");
64+
65+
if constexpr (guardPagesEnabled())
66+
{
67+
try
68+
{
69+
/// Since the stack grows downward, we need to protect the first page
70+
memoryGuardInstall(data, page_size);
71+
}
72+
catch (...)
73+
{
74+
free(data);
75+
throw;
76+
}
77+
}
4978
}
5079
~ThreadStack()
5180
{
52-
mprotect(data, getPageSize(), PROT_WRITE|PROT_READ);
81+
if constexpr (guardPagesEnabled())
82+
memoryGuardRemove(data, getPageSize());
83+
5384
free(data);
5485
}
5586

56-
static size_t getSize() { return std::max<size_t>(UNWIND_MINSIGSTKSZ, MINSIGSTKSZ); }
87+
static size_t getSize()
88+
{
89+
auto size = std::max<size_t>(UNWIND_MINSIGSTKSZ, MINSIGSTKSZ);
90+
91+
if constexpr (guardPagesEnabled())
92+
size += getPageSize();
93+
94+
return size;
95+
}
5796
void * getData() const { return data; }
5897

5998
private:
6099
/// 16 KiB - not too big but enough to handle error.
61-
void * data;
100+
void * data = nullptr;
62101
};
63102

64103
}

src/Common/memory.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <sys/mman.h>
2+
#include <Poco/Environment.h>
3+
#include <Common/Exception.h>
4+
#include <Common/VersionNumber.h>
5+
#include <Common/memory.h>
6+
7+
#ifdef OS_LINUX
8+
#if !defined(MADV_GUARD_INSTALL)
9+
#define MADV_GUARD_INSTALL 102
10+
#endif
11+
12+
#if !defined(MADV_GUARD_REMOVE)
13+
#define MADV_GUARD_REMOVE 103
14+
#endif
15+
16+
static bool supportsGuardPages()
17+
{
18+
DB::VersionNumber madv_guard_minimal_version(6, 13, 0);
19+
DB::VersionNumber linux_version(Poco::Environment::osVersion());
20+
return (linux_version >= madv_guard_minimal_version);
21+
}
22+
static bool supports_guard_pages = supportsGuardPages();
23+
#endif // OS_LINUX
24+
25+
namespace DB::ErrorCodes
26+
{
27+
extern const int SYSTEM_ERROR;
28+
}
29+
30+
/// Uses MADV_GUARD_INSTALL if available, or mprotect() if not
31+
void memoryGuardInstall(void *addr, size_t len)
32+
{
33+
#ifdef OS_LINUX
34+
if (supports_guard_pages)
35+
{
36+
if (madvise(addr, len, MADV_GUARD_INSTALL))
37+
throw DB::ErrnoException(DB::ErrorCodes::SYSTEM_ERROR, "Cannot madvise(MADV_GUARD_INSTALL)");
38+
}
39+
else
40+
#endif
41+
{
42+
if (mprotect(addr, len, PROT_NONE))
43+
throw DB::ErrnoException(DB::ErrorCodes::SYSTEM_ERROR, "Cannot mprotect(PROT_NONE)");
44+
}
45+
}
46+
47+
/// Uses MADV_GUARD_REMOVE if available, or mprotect() if not
48+
void memoryGuardRemove(void *addr, size_t len)
49+
{
50+
#ifdef OS_LINUX
51+
if (supports_guard_pages)
52+
{
53+
if (madvise(addr, len, MADV_GUARD_REMOVE))
54+
throw DB::ErrnoException(DB::ErrorCodes::SYSTEM_ERROR, "Cannot madvise(MADV_GUARD_INSTALL)");
55+
}
56+
else
57+
#endif
58+
{
59+
if (mprotect(addr, len, PROT_READ|PROT_WRITE))
60+
throw DB::ErrnoException(DB::ErrorCodes::SYSTEM_ERROR, "Cannot mprotect(PROT_NONE)");
61+
}
62+
}

0 commit comments

Comments
 (0)