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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
#pragma once
#include <cstdint>
#include <cstdlib>
#include <mutex>
#ifndef RLBOX_USE_CUSTOM_SHARED_LOCK
# include <shared_mutex>
#endif
#include <utility>
#include "rlbox_helpers.hpp"
namespace rlbox {
class rlbox_noop_sandbox;
struct rlbox_noop_sandbox_thread_data
{
rlbox_noop_sandbox* sandbox;
uint32_t last_callback_invoked;
};
#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
rlbox_noop_sandbox_thread_data* get_rlbox_noop_sandbox_thread_data();
# define RLBOX_NOOP_SANDBOX_STATIC_VARIABLES() \
thread_local rlbox::rlbox_noop_sandbox_thread_data \
rlbox_noop_sandbox_thread_info{ 0, 0 }; \
namespace rlbox { \
rlbox_noop_sandbox_thread_data* get_rlbox_noop_sandbox_thread_data() \
{ \
return &rlbox_noop_sandbox_thread_info; \
} \
} \
static_assert(true, "Enforce semi-colon")
#endif
/**
* @brief Class that implements the null sandbox. This sandbox doesn't actually
* provide any isolation and only serves as a stepping stone towards migrating
* an application to use the RLBox API.
*/
class rlbox_noop_sandbox
{
public:
// Stick with the system defaults
using T_LongLongType = long long;
using T_LongType = long;
using T_IntType = int;
using T_PointerType = void*;
using T_ShortType = short;
// no-op sandbox can transfer buffers as there is no sandboxings
// Thus transfer is a noop
using can_grant_deny_access = void;
private:
RLBOX_SHARED_LOCK(callback_mutex);
static inline const uint32_t MAX_CALLBACKS = 64;
void* callback_unique_keys[MAX_CALLBACKS]{ 0 };
void* callbacks[MAX_CALLBACKS]{ 0 };
#ifndef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
thread_local static inline rlbox_noop_sandbox_thread_data thread_data{ 0, 0 };
#endif
template<uint32_t N, typename T_Ret, typename... T_Args>
static T_Ret callback_trampoline(T_Args... params)
{
#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
auto& thread_data = *get_rlbox_noop_sandbox_thread_data();
#endif
thread_data.last_callback_invoked = N;
using T_Func = T_Ret (*)(T_Args...);
T_Func func;
{
#ifndef RLBOX_SINGLE_THREADED_INVOCATIONS
RLBOX_ACQUIRE_SHARED_GUARD(lock, thread_data.sandbox->callback_mutex);
#endif
func = reinterpret_cast<T_Func>(thread_data.sandbox->callbacks[N]);
}
// Callbacks are invoked through function pointers, cannot use std::forward
// as we don't have caller context for T_Args, which means they are all
// effectively passed by value
return func(params...);
}
protected:
inline void impl_create_sandbox() {}
inline void impl_destroy_sandbox() {}
template<typename T>
inline void* impl_get_unsandboxed_pointer(T_PointerType p) const
{
return p;
}
template<typename T>
inline T_PointerType impl_get_sandboxed_pointer(const void* p) const
{
return const_cast<T_PointerType>(p);
}
template<typename T>
static inline void* impl_get_unsandboxed_pointer_no_ctx(
T_PointerType p,
const void* /* example_unsandboxed_ptr */,
rlbox_noop_sandbox* (* // Func ptr
/* param: expensive_sandbox_finder */)(
const void* example_unsandboxed_ptr))
{
return p;
}
template<typename T>
static inline T_PointerType impl_get_sandboxed_pointer_no_ctx(
const void* p,
const void* /* example_unsandboxed_ptr */,
rlbox_noop_sandbox* (* // Func ptr
/* param: expensive_sandbox_finder */)(
const void* example_unsandboxed_ptr))
{
return const_cast<T_PointerType>(p);
}
inline T_PointerType impl_malloc_in_sandbox(size_t size)
{
void* p = malloc(size);
return p;
}
inline void impl_free_in_sandbox(T_PointerType p) { free(p); }
static inline bool impl_is_in_same_sandbox(const void*, const void*)
{
return true;
}
inline bool impl_is_pointer_in_sandbox_memory(const void*) { return true; }
inline bool impl_is_pointer_in_app_memory(const void*) { return true; }
inline size_t impl_get_total_memory()
{
return std::numeric_limits<size_t>::max();
}
inline void* impl_get_memory_location()
{
// There isn't any sandbox memory for the noop_sandbox as we just redirect
// to the app. Also, this is mostly used for pointer swizzling or sandbox
// bounds checks which is also not present/not required. So we can just
// return null
return nullptr;
}
// adding a template so that we can use static_assert to fire only if this
// function is invoked
template<typename T = void>
void* impl_lookup_symbol(const char* /* func_name */)
{
// Will fire if this impl_lookup_symbol is ever called for the static
// sandbox
constexpr bool fail = std::is_same_v<T, void>;
rlbox_detail_static_fail_because(
fail,
"The no_op_sandbox uses static calls and thus developers should add\n\n"
"#define RLBOX_USE_STATIC_CALLS() rlbox_noop_sandbox_lookup_symbol\n\n"
"to their code, to ensure that static calls are handled correctly.");
return nullptr;
}
#define rlbox_noop_sandbox_lookup_symbol(func_name) \
reinterpret_cast<void*>(&func_name) /* NOLINT */
template<typename T, typename T_Converted, typename... T_Args>
auto impl_invoke_with_func_ptr(T_Converted* func_ptr, T_Args&&... params)
{
#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
auto& thread_data = *get_rlbox_noop_sandbox_thread_data();
#endif
auto old_sandbox = thread_data.sandbox;
thread_data.sandbox = this;
auto on_exit =
detail::make_scope_exit([&] { thread_data.sandbox = old_sandbox; });
return (*func_ptr)(params...);
}
template<typename T_Ret, typename... T_Args>
inline T_PointerType impl_register_callback(void* key, void* callback)
{
RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex);
void* chosen_trampoline = nullptr;
// need a compile time for loop as we we need I to be a compile time value
// this is because we are returning the I'th callback trampoline
detail::compile_time_for<MAX_CALLBACKS>([&](auto I) {
if (!chosen_trampoline && callback_unique_keys[I.value] == nullptr) {
callback_unique_keys[I.value] = key;
callbacks[I.value] = callback;
chosen_trampoline = reinterpret_cast<void*>(
callback_trampoline<I.value, T_Ret, T_Args...>);
}
});
return reinterpret_cast<T_PointerType>(chosen_trampoline);
}
static inline std::pair<rlbox_noop_sandbox*, void*>
impl_get_executed_callback_sandbox_and_key()
{
#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
auto& thread_data = *get_rlbox_noop_sandbox_thread_data();
#endif
auto sandbox = thread_data.sandbox;
auto callback_num = thread_data.last_callback_invoked;
void* key = sandbox->callback_unique_keys[callback_num];
return std::make_pair(sandbox, key);
}
template<typename T_Ret, typename... T_Args>
inline void impl_unregister_callback(void* key)
{
RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex);
for (uint32_t i = 0; i < MAX_CALLBACKS; i++) {
if (callback_unique_keys[i] == key) {
callback_unique_keys[i] = nullptr;
callbacks[i] = nullptr;
break;
}
}
}
template<typename T>
inline T* impl_grant_access(T* src, size_t num, bool& success)
{
RLBOX_UNUSED(num);
success = true;
return src;
}
template<typename T>
inline T* impl_deny_access(T* src, size_t num, bool& success)
{
RLBOX_UNUSED(num);
success = true;
return src;
}
};
}
|