LCOV - code coverage report
Current view: top level - src/mem - guard_page.cpp (source / functions) Coverage Total Hit
Test: HPActor Coverage Lines: 62.9 % 62 39
Test Date: 2026-05-20 02:24:49 Functions: 85.7 % 7 6
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : // Copyright 2026 HPActor Contributors
       2                 :             : //
       3                 :             : // Licensed under the Apache License, Version 2.0 (the "License");
       4                 :             : // you may not use this file except in compliance with the License.
       5                 :             : // You may obtain a copy of the License at
       6                 :             : //
       7                 :             : //     http://www.apache.org/licenses/LICENSE-2.0
       8                 :             : //
       9                 :             : // Unless required by applicable law or agreed to in writing, software
      10                 :             : // distributed under the License is distributed on an "AS IS" BASIS,
      11                 :             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12                 :             : // See the License for the specific language governing permissions and
      13                 :             : // limitations under the License.
      14                 :             : 
      15                 :             : #include <hpactor/mem/guard_page.hpp>
      16                 :             : #include <hpactor/platform.hpp>
      17                 :             : 
      18                 :             : #include <cstdio>
      19                 :             : #include <cstdlib>
      20                 :             : #include <cstring>
      21                 :             : #include <sys/mman.h>
      22                 :             : #include <unistd.h>
      23                 :             : 
      24                 :             : namespace hpactor::mem {
      25                 :             : 
      26                 :          64 : size_t page_size() noexcept {
      27                 :          64 :     static size_t ps = static_cast<size_t>(sysconf(_SC_PAGESIZE));
      28                 :          64 :     return ps;
      29                 :             : }
      30                 :             : 
      31                 :          30 : void* guarded_alloc(size_t user_bytes) noexcept {
      32                 :          30 :     size_t ps = page_size();
      33                 :             :     // Ensure at least one byte of usable space, even for user_bytes == 0,
      34                 :             :     // so that the returned pointer is always inside the writable region
      35                 :             :     // (not on the trailing guard page).
      36                 :             :     // Layout: [guard page] [usable (page-aligned)] [guard page]
      37                 :          30 :     size_t clamped = user_bytes > 0 ? user_bytes : 1;
      38                 :          30 :     size_t user_pages = ((clamped + ps - 1) / ps) * ps;
      39                 :          30 :     size_t total = ps + user_pages + ps;
      40                 :             : 
      41                 :          30 :     void* base = mmap(nullptr, total, PROT_READ | PROT_WRITE,
      42                 :             :                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
      43                 :          30 :     if (base == MAP_FAILED)
      44                 :           0 :         return nullptr;
      45                 :             : 
      46                 :             :     // Protect leading guard page
      47                 :          30 :     if (mprotect(base, ps, PROT_NONE) < 0) {
      48                 :           0 :         munmap(base, total);
      49                 :           0 :         return nullptr;
      50                 :             :     }
      51                 :             : 
      52                 :             :     // Protect trailing guard page (page-aligned)
      53                 :          30 :     auto* trailing = static_cast<std::byte*>(base) + ps + user_pages;
      54                 :          30 :     if (mprotect(trailing, ps, PROT_NONE) < 0) {
      55                 :           0 :         mprotect(base, ps, PROT_READ | PROT_WRITE);
      56                 :           0 :         munmap(base, total);
      57                 :           0 :         return nullptr;
      58                 :             :     }
      59                 :             : 
      60                 :          30 :     return static_cast<std::byte*>(base) + ps;
      61                 :             : }
      62                 :             : 
      63                 :          31 : void guarded_free(void* user_ptr, size_t user_bytes) noexcept {
      64                 :          31 :     if (!user_ptr)
      65                 :           1 :         return;
      66                 :          30 :     size_t ps = page_size();
      67                 :             :     // Must match the same clamping as guarded_alloc for consistency
      68                 :          30 :     size_t clamped = user_bytes > 0 ? user_bytes : 1;
      69                 :          30 :     size_t user_pages = ((clamped + ps - 1) / ps) * ps;
      70                 :          30 :     void* base = static_cast<std::byte*>(user_ptr) - ps;
      71                 :          30 :     size_t total = ps + user_pages + ps;
      72                 :          30 :     munmap(base, total);
      73                 :             : }
      74                 :             : 
      75                 :             : // ---------------------------------------------------------------------------
      76                 :             : // Corruption signal handler
      77                 :             : // ---------------------------------------------------------------------------
      78                 :             : 
      79                 :             : namespace {
      80                 :             : // Previous signal handler (chained on non-corruption faults)
      81                 :             : struct sigaction g_prev_action;
      82                 :             : bool g_handler_installed = false;
      83                 :             : 
      84                 :             : // Pre-opened fd for signal-safe logging. Use write() instead of the logger
      85                 :             : // in signal context — logger CAS atomics may deadlock.
      86                 :             : int g_guard_page_fd = -1;
      87                 :             : 
      88                 :           0 : void corruption_sigaction(int sig, siginfo_t* info, void* ctx) {
      89                 :           0 :     if (!info || !info->si_addr) {
      90                 :             :         // Chain to previous handler
      91                 :           0 :         if (g_prev_action.sa_sigaction) {
      92                 :           0 :             g_prev_action.sa_sigaction(sig, info, ctx);
      93                 :             :         }
      94                 :           0 :         return;
      95                 :             :     }
      96                 :             : 
      97                 :           0 :     void* fault_addr = info->si_addr;
      98                 :             : 
      99                 :             :     // Try to identify the owning segment
     100                 :           0 :     auto seg_info = SegmentProvider::instance().lookup(fault_addr);
     101                 :           0 :     if (seg_info.base != nullptr) {
     102                 :             :         // This is our memory — corruption detected
     103                 :             :         // Use direct write() to pre-opened fd — never use the logger in
     104                 :             :         // signal context (CAS atomics may deadlock).
     105                 :           0 :         if (g_guard_page_fd >= 0) {
     106                 :           0 :             const char* msg = "HPActor: guard page violation at %p (segment "
     107                 :             :                               "base %p, size %zu) — memory corruption\n";
     108                 :             :             char buf[256];
     109                 :           0 :             int len = snprintf(buf, sizeof(buf), msg, fault_addr, seg_info.base,
     110                 :             :                                seg_info.size);
     111                 :           0 :             write(g_guard_page_fd, buf, static_cast<size_t>(len));
     112                 :             :         }
     113                 :           0 :         _exit(EXIT_FAILURE);
     114                 :             :     }
     115                 :             : 
     116                 :             :     // Not our memory — chain to previous handler
     117                 :           0 :     if (g_prev_action.sa_sigaction) {
     118                 :           0 :         g_prev_action.sa_sigaction(sig, info, ctx);
     119                 :             :     } else {
     120                 :             :         // No previous handler, restore default and re-raise
     121                 :           0 :         signal(sig, SIG_DFL);
     122                 :           0 :         raise(sig);
     123                 :             :     }
     124                 :             : }
     125                 :             : } // namespace
     126                 :             : 
     127                 :           2 : void set_guard_page_log_fd(int fd) noexcept {
     128                 :           2 :     g_guard_page_fd = fd;
     129                 :           2 : }
     130                 :             : 
     131                 :           3 : void install_corruption_handler() noexcept {
     132                 :           3 :     if (g_handler_installed)
     133                 :           1 :         return;
     134                 :             : 
     135                 :             :     struct sigaction sa;
     136                 :           2 :     std::memset(&sa, 0, sizeof(sa));
     137                 :           2 :     sa.sa_sigaction = corruption_sigaction;
     138                 :           2 :     sa.sa_flags = SA_SIGINFO | SA_NODEFER;
     139                 :             : 
     140                 :           2 :     sigaction(SIGSEGV, &sa, &g_prev_action);
     141                 :           2 :     g_handler_installed = true;
     142                 :             : }
     143                 :             : 
     144                 :           2 : void remove_corruption_handler() noexcept {
     145                 :           2 :     if (!g_handler_installed)
     146                 :           1 :         return;
     147                 :             : 
     148                 :           1 :     sigaction(SIGSEGV, &g_prev_action, nullptr);
     149                 :           1 :     g_handler_installed = false;
     150                 :             : }
     151                 :             : 
     152                 :             : } // namespace hpactor::mem
        

Generated by: LCOV version 2.0-1