microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
fallible_address_space.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
10#include <cstddef>
11#include <cstdint>
12#include <cstring>
13#include <functional>
14#include <setjmp.h>
15#include <signal.h>
16#include <type_traits>
17#include <unistd.h>
18
19// clang-format off
20#include "../microfmt.hpp"
21#include <reloco/tls_provider.hpp>
22#include "address_space.hpp"
23// clang-format on
24
25/*
26 * NOTE: This file works only on targets with support for POSIX signals
27 */
28
29namespace microfmt {
30
35
36namespace detail {
37
42 sigjmp_buf env;
43 bool active{false};
44};
45
50
51// Convenience alias for the tag-differentiated TLS state
52using fallible_tls = reloco::tls_provider<fault_recovery_context *, fallible_space_tls_tag>;
53
66[[nodiscard]] inline fault_recovery_context *fallible_tls_get() noexcept {
67 auto result = fallible_tls::get();
68 if (!result)
69 return nullptr;
70 using value_type = std::decay_t<decltype(*result)>;
71 if constexpr (std::is_same_v<value_type, std::reference_wrapper<fault_recovery_context *>>) {
72 return result->get();
73 } else {
74 return *result;
75 }
76}
77
81inline bool fallible_tls_set(fault_recovery_context *ctx) noexcept { return fallible_tls::set(ctx).has_value(); }
82
83inline void fallible_signal_handler(int sig, siginfo_t *, void *) noexcept {
84 auto *ctx = fallible_tls_get();
85 if (ctx && ctx->active) {
86 siglongjmp(ctx->env, sig);
87 }
88
89 // If no fallible operation is active, restore default signal handler and re-raise
90 struct sigaction sa{};
91 sa.sa_handler = SIG_DFL;
92 sigemptyset(&sa.sa_mask);
93 sigaction(sig, &sa, nullptr);
94 raise(sig);
95}
96
97inline bool install_fallible_handlers() noexcept {
98 static bool initialized = []() {
99 struct sigaction sa{};
100 sa.sa_sigaction = fallible_signal_handler;
101 sa.sa_flags = SA_SIGINFO;
102 sigemptyset(&sa.sa_mask);
103 sigaction(SIGSEGV, &sa, nullptr);
104 sigaction(SIGBUS, &sa, nullptr);
105 return true;
106 }();
107 return initialized;
108}
109
110} // namespace detail
111
116 using context_type = void;
117
118 static bool read_bytes(uintptr_t addr, void *dest, size_t size) noexcept {
119 if (addr == 0)
120 return false;
121 if (size == 0)
122 return true;
123
125
127 fault_ctx.active = true;
128
129 // Nesting-safe context management via the tag-differentiated TLS state
130 auto *old_ctx = detail::fallible_tls_get();
131 if (!detail::fallible_tls_set(&fault_ctx))
132 return false;
133
134 if (sigsetjmp(fault_ctx.env, 1) != 0) {
136 return false;
137 }
138
139 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
140 std::memcpy(dest, reinterpret_cast<const void *>(addr), size);
141 RELOCO_END_UNSAFE_BUFFER_USAGE;
142
144 return true;
145 }
146
147 static bool write_bytes(uintptr_t addr, const void *src, size_t size) noexcept {
148 if (addr == 0)
149 return false;
150 if (size == 0)
151 return true;
152
154
156 fault_ctx.active = true;
157
158 auto *old_ctx = detail::fallible_tls_get();
159 if (!detail::fallible_tls_set(&fault_ctx))
160 return false;
161
162 if (sigsetjmp(fault_ctx.env, 1) != 0) {
164 return false;
165 }
166
167 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
168 std::memcpy(reinterpret_cast<void *>(addr), src, size);
169 RELOCO_END_UNSAFE_BUFFER_USAGE;
170
172 return true;
173 }
174
175 static bool read_string(uintptr_t addr, char *dest, size_t max_len, size_t &out_len, bool &null_term) noexcept {
176 if (addr == 0)
177 return false;
178
180
182 fault_ctx.active = true;
183
184 auto *old_ctx = detail::fallible_tls_get();
185 if (!detail::fallible_tls_set(&fault_ctx))
186 return false;
187
188 if (sigsetjmp(fault_ctx.env, 1) != 0) {
190 return false;
191 }
192
193 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
194 const char *src = reinterpret_cast<const char *>(addr);
195 size_t i = 0;
196 while (i < max_len) {
197 dest[i] = src[i];
198 if (dest[i] == '\0') {
199 out_len = i;
200 null_term = true;
202 return true;
203 }
204 ++i;
205 }
206 RELOCO_END_UNSAFE_BUFFER_USAGE;
207
209 out_len = max_len;
210 null_term = false;
211 return true;
212 }
213};
214
215} // namespace microfmt
Type-erased remote address-space access and remote string/object views.
#define MICROFMT_API_CLASS
Definition compat.hpp:181
fault_recovery_context * fallible_tls_get() noexcept
Unwraps fallible_tls::get()'s result to a plain pointer.
Definition fallible_address_space.hpp:66
bool install_fallible_handlers() noexcept
Definition fallible_address_space.hpp:97
reloco::tls_provider< fault_recovery_context *, fallible_space_tls_tag > fallible_tls
Definition fallible_address_space.hpp:52
void fallible_signal_handler(int sig, siginfo_t *, void *) noexcept
Definition fallible_address_space.hpp:83
bool fallible_tls_set(fault_recovery_context *ctx) noexcept
Sets the calling thread's recovery-context slot; returns false if the underlying tls_provider::set() ...
Definition fallible_address_space.hpp:81
Definition assert.hpp:19
void context_type
Definition fallible_address_space.hpp:116
static bool write_bytes(uintptr_t addr, const void *src, size_t size) noexcept
Definition fallible_address_space.hpp:147
static bool read_bytes(uintptr_t addr, void *dest, size_t size) noexcept
Definition fallible_address_space.hpp:118
static bool read_string(uintptr_t addr, char *dest, size_t max_len, size_t &out_len, bool &null_term) noexcept
Definition fallible_address_space.hpp:175
Definition address_space.hpp:37
Unique tag to isolate the fallible address space's TLS storage slot.
Definition fallible_address_space.hpp:49
Context holding the jump buffer and active state for signal-safe memory fault recovery.
Definition fallible_address_space.hpp:41
bool active
Definition fallible_address_space.hpp:43
sigjmp_buf env
Definition fallible_address_space.hpp:42
Tag identifying the fallible local address space.
Definition fallible_address_space.hpp:34