1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
#pragma once
#include "stack_common.h"
#include <util/generic/array_ref.h>
#include <util/generic/strbuf.h>
#include <util/system/protect.h>
namespace NCoro::NStack {
/*! Guard detect stack overflow/override, by setting memory before and after stack with predefined values/properties.
* Actually, it sets memory only after the end of stack workspace memory - previous guard section should be set
* already (for previous stack in case of pool allocator) and can be checked on demand.
* Stack pointer should be page-aligned.
*/
//! Checks integrity by writing a predefined sequence and comparing it with original
class TCanaryGuard final {
public:
//! Size of guard section in bytes
static constexpr size_t GetSize() { return Canary.size(); }
//! Size of page-aligned guard section in bytes
static constexpr size_t GetPageAlignedSize() { return AlignedSize_; }
//! Get stack memory between guard sections
static TArrayRef<char> GetWorkspace(void* stack, size_t size) noexcept {
Y_ASSERT( !((size_t)stack & PageSizeMask) );
Y_ASSERT( !(size & PageSizeMask) );
Y_ASSERT(size > Canary.size());
return {(char*)stack, size - Canary.size()};
}
/*! Set guard section before the end of stack memory (at stack + size - guard size position)
* checkPrevious: check guard before stack memory for integrity
*/
static void Protect(void* stack, size_t size, bool checkPrevious) noexcept {
Y_ASSERT( !((size_t)stack & PageSizeMask) ); // stack pointer should be page aligned
Y_ASSERT( !(size & PageSizeMask) ); // stack size should be page aligned
Y_ASSERT(size >= Canary.size()); // stack should have enough space to place guard
if (checkPrevious) {
Y_ABORT_UNLESS(CheckOverflow(stack), "Previous stack was corrupted");
}
auto guardPos = (char*) stack + size - Canary.size();
memcpy(guardPos, Canary.data(), Canary.size());
}
//! This guard doesn't change memory flags
static constexpr void RemoveProtection(void*, size_t) {}
//! Should remove protection before returning memory to system
static constexpr bool ShouldRemoveProtectionBeforeFree() { return false; }
static bool CheckOverflow(void* stack) noexcept {
Y_ASSERT(stack);
char* guardPos = (char*) ((size_t)stack - Canary.size());
return TStringBuf(guardPos, Canary.size()) == Canary;
}
static bool CheckOverride(void* stack, size_t size) noexcept {
Y_ASSERT(stack);
Y_ASSERT(size > Canary.size());
char* guardPos = (char*) ((size_t)stack + size - Canary.size());
return TStringBuf(guardPos, Canary.size()) == Canary;
}
private:
static constexpr TStringBuf Canary = "[ThisIsACanaryCoroutineStackGuardIfYouReadThisTheStackIsStillOK]";
static_assert(Canary.size() == 64);
static constexpr size_t AlignedSize_ = (Canary.size() + PageSize - 1) & ~PageSizeMask;
};
// ------------------------------------------------------------------------
//
//! Ensures integrity by removing access rights for border pages
class TPageGuard final {
public:
//! Size of guard section in bytes
static constexpr size_t GetSize() { return PageSize; }
//! Size of page-aligned guard section in bytes
static constexpr size_t GetPageAlignedSize() { return PageSize; }
static TArrayRef<char> GetWorkspace(void* stack, size_t size) noexcept {
Y_ASSERT( !((size_t)stack & PageSizeMask) );
Y_ASSERT( !(size & PageSizeMask) );
Y_ASSERT(size > PageSize);
return {(char*)stack, size - PageSize};
}
static void Protect(void* stack, size_t size, bool /*checkPrevious*/) noexcept {
Y_ASSERT( !((size_t)stack & PageSizeMask) ); // stack pointer should be page aligned
Y_ASSERT( !(size & PageSizeMask) ); // stack size should be page aligned
Y_ASSERT(size >= PageSize); // stack should have enough space to place guard
ProtectMemory((char*)stack + size - PageSize, PageSize, PM_NONE);
}
//! Remove protection, to allow stack memory be freed
static void RemoveProtection(void* stack, size_t size) noexcept {
Y_ASSERT( !((size_t)stack & PageSizeMask) );
Y_ASSERT( !(size & PageSizeMask) );
Y_ASSERT(size >= PageSize);
ProtectMemory((char*)stack + size - PageSize, PageSize, PM_WRITE | PM_READ);
}
//! Should remove protection before returning memory to system
static constexpr bool ShouldRemoveProtectionBeforeFree() { return true; }
//! For page guard is not used - it crashes process at once in this case.
static constexpr bool CheckOverflow(void*) { return true; }
static constexpr bool CheckOverride(void*, size_t) { return true; }
};
template<typename TGuard>
const TGuard& GetGuard() noexcept;
}
|