6
6
#include < utility>
7
7
8
8
namespace phoenix {
9
+ // / \brief A helper class for preventing stack corruption.
10
+ // /
11
+ // / This class can be used to guard against stack corruption when a value is expected to be
12
+ // / pushed onto the stack but the push does not happen for some reason. Unless #inhibit is called,
13
+ // / when an instance of this class is destructed, a default value of the given datatype is pushed
14
+ // / into the stack.
15
+ // /
16
+ // / \code
17
+ // / // construct the stack guard
18
+ // / stack_guard guard {this, datatype::int_};
19
+ // /
20
+ // / // ... do something that might fail to push an int to the stack ...
21
+ // /
22
+ // / // make sure to inhibit the guard if pushing the value succeeded
23
+ // / guard.inhibit();
24
+ // / \endcode
25
+ struct stack_guard {
26
+ public:
27
+ // / \brief Creates a new stack guard.
28
+ // / \param machine The VM this instance is guarding.
29
+ // / \param type The type of value to push if the guard is triggered.
30
+ stack_guard (vm* machine, datatype type) : _m_type(type), _m_machine(machine) {}
31
+
32
+ // / \brief Triggers this guard.
33
+ // /
34
+ // / Unless #inhibit was called, this destructor will push a value of the datatype passed in the
35
+ // / constructor onto the stack of the VM passed in the constructor.
36
+ ~stack_guard () {
37
+ if (_m_inhibited)
38
+ return ;
39
+
40
+ switch (_m_type) {
41
+ case datatype::void_:
42
+ break ;
43
+ case datatype::float_:
44
+ _m_machine->push_float (0 );
45
+ break ;
46
+ case datatype::integer:
47
+ case datatype::function:
48
+ _m_machine->push_int (0 );
49
+ break ;
50
+ case datatype::string:
51
+ _m_machine->push_string (" " );
52
+ break ;
53
+ case datatype::instance:
54
+ _m_machine->push_instance (nullptr );
55
+ break ;
56
+ case datatype::class_:
57
+ break ;
58
+ case datatype::prototype:
59
+ break ;
60
+ }
61
+ }
62
+
63
+ // / \brief Inhibits this guard.
64
+ // /
65
+ // / Calling this function will cause the guard to disengage and thus not push a
66
+ // / value onto the stack.
67
+ void inhibit () {
68
+ _m_inhibited = true ;
69
+ }
70
+
71
+ private:
72
+ datatype _m_type;
73
+ vm* _m_machine;
74
+ bool _m_inhibited {false };
75
+ };
76
+
9
77
vm::vm (script&& scr, std::uint8_t flags) : script(std::move(scr)), _m_flags(flags) {
10
78
_m_self_sym = find_symbol_by_name (" SELF" );
11
79
_m_other_sym = find_symbol_by_name (" OTHER" );
@@ -128,9 +196,15 @@ namespace phoenix {
128
196
// Check if the function is overridden and if it is, call the resulting external.
129
197
auto cb = _m_function_overrides.find (instr.address );
130
198
if (cb != _m_function_overrides.end ()) {
199
+ // Guard against exceptions during external invocation.
200
+ stack_guard guard {this , sym->rtype ()};
201
+
131
202
push_call (sym);
132
203
cb->second (*this );
133
204
pop_call ();
205
+
206
+ // The stack is left intact.
207
+ guard.inhibit ();
134
208
} else {
135
209
sym = find_symbol_by_address (instr.address );
136
210
if (sym == nullptr ) {
@@ -148,6 +222,9 @@ namespace phoenix {
148
222
throw vm_exception {" be: no external found for index" };
149
223
}
150
224
225
+ // Guard against exceptions during external invocation.
226
+ stack_guard guard {this , sym->rtype ()};
227
+
151
228
auto cb = _m_externals.find (sym);
152
229
if (cb == _m_externals.end ()) {
153
230
if (_m_default_external.has_value ()) {
@@ -161,6 +238,9 @@ namespace phoenix {
161
238
push_call (sym);
162
239
cb->second (*this );
163
240
pop_call ();
241
+
242
+ // The stack is left intact.
243
+ guard.inhibit ();
164
244
break ;
165
245
}
166
246
case opcode::pushi:
0 commit comments