aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/coroutine/engine/stack/stack_guards.h
blob: c354339ed244ec27eda5e0a1f6ae825da081168f (plain) (blame)
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 uint64_t GetSize() { return Canary.size(); } 
        //! Size of page-aligned guard section in bytes 
        static constexpr uint64_t GetPageAlignedSize() { return AlignedSize_; } 
 
        //! Get stack memory between guard sections 
        static TArrayRef<char> GetWorkspace(void* stack, uint64_t size) noexcept { 
            Y_ASSERT( !((uint64_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, uint64_t size, bool checkPrevious) noexcept { 
            Y_ASSERT( !((uint64_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_VERIFY(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*, uint64_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*) ((uint64_t)stack - Canary.size()); 
            return TStringBuf(guardPos, Canary.size()) == Canary; 
        } 
 
        static bool CheckOverride(void* stack, uint64_t size) noexcept { 
            Y_ASSERT(stack); 
            Y_ASSERT(size > Canary.size()); 
 
            char* guardPos = (char*) ((uint64_t)stack + size - Canary.size()); 
            return TStringBuf(guardPos, Canary.size()) == Canary; 
        } 
 
    private: 
        static constexpr TStringBuf Canary = "[ThisIsACanaryCoroutineStackGuardIfYouReadThisTheStackIsStillOK]";
        static_assert(Canary.size() == 64); 
        static constexpr uint64_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 uint64_t GetSize() { return PageSize; } 
        //! Size of page-aligned guard section in bytes 
        static constexpr uint64_t GetPageAlignedSize() { return PageSize; } 
 
        static TArrayRef<char> GetWorkspace(void* stack, uint64_t size) noexcept { 
            Y_ASSERT( !((uint64_t)stack & PageSizeMask) ); 
            Y_ASSERT( !(size & PageSizeMask) ); 
            Y_ASSERT(size > PageSize); 
 
            return {(char*)stack, size - PageSize}; 
        } 
 
        static void Protect(void* stack, uint64_t size, bool /*checkPrevious*/) noexcept { 
            Y_ASSERT( !((uint64_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, uint64_t size) noexcept { 
            Y_ASSERT( !((uint64_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*, uint64_t) { return true; } 
    }; 
 
 
    template<typename TGuard> 
    const TGuard& GetGuard() noexcept; 
}