Skip to content

Commit 56775ce

Browse files
committed
[#25] vm: add basic stack_guard for preventing stack corruption
This is currently only applied wherever external C++ function are called to prevent internal exceptions corrupting the VM's stack. (cherry picked from commit b704f7e)
1 parent 1e4c52c commit 56775ce

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

source/vm.cc

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,74 @@
66
#include <utility>
77

88
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+
977
vm::vm(script&& scr, std::uint8_t flags) : script(std::move(scr)), _m_flags(flags) {
1078
_m_self_sym = find_symbol_by_name("SELF");
1179
_m_other_sym = find_symbol_by_name("OTHER");
@@ -128,9 +196,15 @@ namespace phoenix {
128196
// Check if the function is overridden and if it is, call the resulting external.
129197
auto cb = _m_function_overrides.find(instr.address);
130198
if (cb != _m_function_overrides.end()) {
199+
// Guard against exceptions during external invocation.
200+
stack_guard guard {this, sym->rtype()};
201+
131202
push_call(sym);
132203
cb->second(*this);
133204
pop_call();
205+
206+
// The stack is left intact.
207+
guard.inhibit();
134208
} else {
135209
sym = find_symbol_by_address(instr.address);
136210
if (sym == nullptr) {
@@ -148,6 +222,9 @@ namespace phoenix {
148222
throw vm_exception {"be: no external found for index"};
149223
}
150224

225+
// Guard against exceptions during external invocation.
226+
stack_guard guard {this, sym->rtype()};
227+
151228
auto cb = _m_externals.find(sym);
152229
if (cb == _m_externals.end()) {
153230
if (_m_default_external.has_value()) {
@@ -161,6 +238,9 @@ namespace phoenix {
161238
push_call(sym);
162239
cb->second(*this);
163240
pop_call();
241+
242+
// The stack is left intact.
243+
guard.inhibit();
164244
break;
165245
}
166246
case opcode::pushi:

0 commit comments

Comments
 (0)