stack_guards.h 5.1 KB

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