Skip to content

Commit 5676194

Browse files
committedApr 8, 2016
Fixed BytecodeVerifier for CompiledCode#literals change.
1 parent ec57d31 commit 5676194

7 files changed

+409
-390
lines changed
 

Diff for: ‎machine/builtin/compiled_code.cpp

+5-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#include "arguments.hpp"
2-
#include "bytecode_verification.hpp"
2+
#include "bytecode_verifier.hpp"
33
#include "call_frame.hpp"
44
#include "configuration.hpp"
55
#include "instruments/timing.hpp"
@@ -112,9 +112,7 @@ namespace rubinius {
112112
return as<Fixnum>(lines()->at(fin+1))->to_native();
113113
}
114114

115-
MachineCode* CompiledCode::internalize(STATE,
116-
const char** reason, int* ip)
117-
{
115+
MachineCode* CompiledCode::internalize(STATE) {
118116
MachineCode* mcode = machine_code();
119117

120118
atomic::memory_barrier();
@@ -129,13 +127,8 @@ namespace rubinius {
129127
mcode = self->machine_code();
130128
if(!mcode) {
131129
{
132-
BytecodeVerification bv(self);
133-
if(!bv.verify(state)) {
134-
if(reason) *reason = bv.failure_reason();
135-
if(ip) *ip = bv.failure_ip();
136-
std::cerr << "Error validating bytecode: " << bv.failure_reason() << "\n";
137-
return 0;
138-
}
130+
BytecodeVerifier bytecode_verifier(self);
131+
bytecode_verifier.verify(state);
139132
}
140133

141134
mcode = new MachineCode(state, self);
@@ -197,18 +190,12 @@ namespace rubinius {
197190
{
198191
CompiledCode* code = as<CompiledCode>(exec);
199192
if(code->execute == default_executor) {
200-
const char* reason = 0;
201-
int ip = -1;
202-
203193
OnStack<5> os(state, code, exec, mod, args.recv_location(), args.block_location());
204194

205195
memory::VariableRootBuffer vrb(state->vm()->current_root_buffers(),
206196
&args.arguments_location(), args.total());
207197

208-
if(!code->internalize(state, &reason, &ip)) {
209-
Exception::bytecode_error(state, code, ip, reason);
210-
return 0;
211-
}
198+
if(!code->internalize(state)) return 0;
212199
}
213200

214201
return code->execute(state, exec, mod, args);

Diff for: ‎machine/builtin/compiled_code.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ namespace rubinius {
103103

104104
void post_marshal(STATE);
105105
size_t number_of_locals();
106-
MachineCode* internalize(STATE, const char** failure_reason=0, int* ip=0);
106+
MachineCode* internalize(STATE);
107107
void specialize(STATE, TypeInfo* ti);
108108

109109
static Object* default_executor(STATE, Executable* exec, Module* mod, Arguments& args);

Diff for: ‎machine/builtin/system.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -1254,10 +1254,7 @@ namespace rubinius {
12541254
if(Class* cls = try_as<Class>(mod)) {
12551255
OnStack<5> o2(state, mod, cc, scope, vis, cls);
12561256

1257-
if(!cc->internalize(state)) {
1258-
Exception::raise_argument_error(state, "invalid bytecode method");
1259-
return 0;
1260-
}
1257+
if(!cc->internalize(state)) return 0;
12611258

12621259
object_type type = (object_type)cls->instance_type()->to_native();
12631260
TypeInfo* ti = state->memory()->type_info[type];

Diff for: ‎machine/bytecode_verification.cpp

-309
This file was deleted.

Diff for: ‎machine/bytecode_verification.hpp

-58
This file was deleted.

Diff for: ‎machine/bytecode_verifier.cpp

+347
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
#include "builtin/compiled_code.hpp"
2+
#include "builtin/exception.hpp"
3+
#include "builtin/fixnum.hpp"
4+
#include "builtin/iseq.hpp"
5+
#include "builtin/string.hpp"
6+
#include "builtin/symbol.hpp"
7+
#include "builtin/tuple.hpp"
8+
9+
#include "bytecode_verifier.hpp"
10+
#include "configuration.hpp"
11+
#include "machine_code.hpp"
12+
#include "object_utils.hpp"
13+
14+
#include "instruments/timing.hpp"
15+
16+
namespace rubinius {
17+
BytecodeVerifier::BytecodeVerifier(CompiledCode* code)
18+
: method_(code)
19+
, ops_(NULL)
20+
, total_(0)
21+
, max_stack_allowed_(0)
22+
, max_stack_seen_(0)
23+
, stack_(0)
24+
, locals_(0)
25+
, max_stack_local_(-1)
26+
{}
27+
28+
BytecodeVerifier::~BytecodeVerifier() {
29+
if(stack_) delete[] stack_;
30+
}
31+
32+
void BytecodeVerifier::fail(STATE, const char* reason, int ip) {
33+
Exception::bytecode_error(state, method_, ip, reason);
34+
}
35+
36+
void BytecodeVerifier::verify_width(STATE, int ip) {
37+
if(ip >= total_) {
38+
fail(state, "truncated instruction sequence", ip);
39+
}
40+
}
41+
42+
Fixnum* BytecodeVerifier::verify_opcode(STATE, int ip) {
43+
verify_width(state, ip);
44+
45+
if(Fixnum* fix = try_as<Fixnum>(ops_->at(ip))) {
46+
return fix;
47+
} else {
48+
fail(state, "instruction is not a Fixnum", ip);
49+
}
50+
}
51+
52+
Fixnum* BytecodeVerifier::verify_argument(STATE, int ip) {
53+
verify_width(state, ip);
54+
55+
if(Fixnum* fix = try_as<Fixnum>(ops_->at(ip))) {
56+
return fix;
57+
} else {
58+
fail(state, "instruction argument is not a Fixnum", ip);
59+
}
60+
}
61+
62+
Object* BytecodeVerifier::verify_object(STATE, int index, int ip) {
63+
if(Object* obj = try_as<Object>(method_->literals()->at(index))) {
64+
return obj;
65+
} else {
66+
fail(state, "instruction argument is not an Object", ip);
67+
}
68+
}
69+
70+
Symbol* BytecodeVerifier::verify_symbol(STATE, int index, int ip) {
71+
if(Symbol* sym = try_as<Symbol>(method_->literals()->at(index))) {
72+
return sym;
73+
} else {
74+
fail(state, "instruction argument is not a Symbol", ip);
75+
}
76+
}
77+
78+
Object* BytecodeVerifier::verify_symbol_or_nil(STATE, int index, int ip) {
79+
Object* val = method_->literals()->at(index);
80+
81+
if(val->nil_p()) {
82+
return val;
83+
} else if(Symbol* sym = try_as<Symbol>(val)) {
84+
return sym;
85+
} else {
86+
fail(state, "instruction argument is not a Symbol or nil", ip);
87+
}
88+
}
89+
90+
Object* BytecodeVerifier::verify_code(STATE, int index, int ip) {
91+
if(CompiledCode* code = try_as<CompiledCode>(method_->literals()->at(index))) {
92+
return force_as<Object>(code);
93+
} else if(String* str = try_as<String>(method_->literals()->at(index))) {
94+
return force_as<Object>(str);
95+
} else {
96+
fail(state, "instruction argument is not a String or CompiledCode", ip);
97+
}
98+
}
99+
100+
void BytecodeVerifier::verify(STATE) {
101+
// Do this setup here instead of the constructor so we can do
102+
// some validation of the CompiledCode's fields we read them.
103+
104+
// Double check the method itself, since it might be a nil
105+
if(!kind_of<CompiledCode>(method_)) {
106+
fail(state, "not passed a CompiledCode object", -1);
107+
}
108+
109+
if(Fixnum* fix = try_as<Fixnum>(method_->local_count())) {
110+
locals_ = fix->to_native();
111+
} else {
112+
fail(state, "local_count is not a Fixnum", -1);
113+
}
114+
115+
InstructionSequence* iseq = try_as<InstructionSequence>(method_->iseq());
116+
if(!iseq) {
117+
fail(state, "iseq is not an InstructionSequence", -1);
118+
}
119+
120+
if(Tuple* tup = try_as<Tuple>(iseq->opcodes())) {
121+
ops_ = tup;
122+
} else {
123+
fail(state, "opcodes is not a Tuple", -1);
124+
}
125+
126+
if(Fixnum* fix = try_as<Fixnum>(method_->stack_size())) {
127+
max_stack_allowed_ = fix->to_native();
128+
} else {
129+
fail(state, "stack_size is not a Fixnum", -1);
130+
}
131+
132+
if(Fixnum* fix = try_as<Fixnum>(method_->splat())) {
133+
if(fix->to_native() >= locals_) {
134+
fail(state, "invalid splat position", -1);
135+
}
136+
}
137+
138+
Fixnum* tot = try_as<Fixnum>(method_->total_args());
139+
if(!tot) {
140+
fail(state, "total_args is not a Fixnum", -1);
141+
}
142+
Fixnum* req = try_as<Fixnum>(method_->required_args());
143+
if(!req) {
144+
fail(state, "required_args is not a Fixnum", -1);
145+
}
146+
Fixnum* post = try_as<Fixnum>(method_->post_args());
147+
if(!post) {
148+
fail(state, "post_args is not a Fixnum", -1);
149+
}
150+
151+
if(tot->to_native() > locals_) {
152+
fail(state, "more arguments than local slots", -1);
153+
}
154+
155+
if(req->to_native() > tot->to_native()) {
156+
fail(state, "more required arguments than total", -1);
157+
}
158+
159+
if(post->to_native() > req->to_native()) {
160+
fail(state, "more post arguments than required", -1);
161+
}
162+
163+
if(post->to_native() > tot->to_native()) {
164+
fail(state, "more post arguments than total", -1);
165+
}
166+
167+
total_ = ops_->num_fields();
168+
stack_ = new int32_t[total_];
169+
170+
for(native_int i = 0; i < total_; i++) {
171+
stack_[i] = -1;
172+
}
173+
174+
std::list<Section> ips;
175+
ips.push_back(Section(0, 0));
176+
177+
while(!ips.empty()) {
178+
Section& section = ips.front();
179+
180+
int ip = section.ip;
181+
int sp = section.sp;
182+
183+
ips.pop_front();
184+
185+
verify_from(state, sp, ip, ips);
186+
}
187+
188+
// Now, check there is a enough space for the stack locals.
189+
if(max_stack_seen_ + max_stack_local_ >= max_stack_allowed_) {
190+
fail(state, "not enough space for stack locals", -1);
191+
}
192+
}
193+
194+
namespace {
195+
#include "gen/instruction_effects.hpp"
196+
}
197+
198+
void BytecodeVerifier::verify_from(STATE, int sp, int ip, std::list<Section>& ips) {
199+
int insn_ip = ip;
200+
201+
if(sp < 0) {
202+
fail(state, "stack underflow error", ip);
203+
}
204+
205+
for(;;) {
206+
int old_sp = stack_[ip];
207+
if(old_sp < 0) {
208+
stack_[ip] = sp;
209+
} else if(old_sp != sp) {
210+
fail(state, "inconsistent stack depth", ip);
211+
} else {
212+
// Already been here and stack is consistent, done.
213+
return;
214+
}
215+
216+
opcode op = static_cast<opcode>(verify_opcode(state, ip++)->to_native());
217+
size_t width = InstructionSequence::instruction_width(op);
218+
219+
opcode arg1 = 0, arg2 = 0, arg3 = 0;
220+
221+
switch(width) {
222+
case 1:
223+
break;
224+
case 2:
225+
arg1 = static_cast<opcode>(verify_argument(state, ip++)->to_native());
226+
break;
227+
case 3:
228+
arg1 = static_cast<opcode>(verify_argument(state, ip++)->to_native());
229+
arg2 = static_cast<opcode>(verify_argument(state, ip++)->to_native());
230+
break;
231+
case 4:
232+
arg1 = static_cast<opcode>(verify_argument(state, ip++)->to_native());
233+
arg2 = static_cast<opcode>(verify_argument(state, ip++)->to_native());
234+
arg3 = static_cast<opcode>(verify_argument(state, ip++)->to_native());
235+
break;
236+
default:
237+
fail(state, "invalid instruction", insn_ip);
238+
}
239+
240+
switch(op) {
241+
case InstructionSequence::insn_push_literal:
242+
case InstructionSequence::insn_push_memo:
243+
verify_object(state, arg1, insn_ip);
244+
break;
245+
case InstructionSequence::insn_create_block:
246+
verify_code(state, arg1, insn_ip);
247+
break;
248+
case InstructionSequence::insn_send_vcall:
249+
case InstructionSequence::insn_send_method:
250+
case InstructionSequence::insn_send_stack:
251+
case InstructionSequence::insn_send_stack_with_block:
252+
case InstructionSequence::insn_send_stack_with_splat:
253+
case InstructionSequence::insn_check_serial:
254+
case InstructionSequence::insn_check_serial_private:
255+
case InstructionSequence::insn_invoke_primitive:
256+
case InstructionSequence::insn_set_ivar:
257+
case InstructionSequence::insn_push_ivar:
258+
case InstructionSequence::insn_set_const:
259+
case InstructionSequence::insn_set_const_at:
260+
case InstructionSequence::insn_object_to_s:
261+
case InstructionSequence::insn_push_const:
262+
case InstructionSequence::insn_find_const:
263+
case InstructionSequence::insn_zsuper:
264+
verify_symbol(state, arg1, insn_ip);
265+
break;
266+
case InstructionSequence::insn_send_super_stack_with_block:
267+
case InstructionSequence::insn_send_super_stack_with_splat:
268+
verify_symbol_or_nil(state, arg1, insn_ip);
269+
break;
270+
}
271+
272+
int read = 0, write = 0;
273+
274+
int effect = stack_difference(op, arg1, arg2, arg3, &read, &write);
275+
276+
// Check for under read
277+
if(sp - read < 0) {
278+
fail(state, "stack underflow on read", insn_ip);
279+
}
280+
281+
// Apply the total effect to propagate it.
282+
sp += effect;
283+
if(sp < 0) {
284+
fail(state, "stack underflow on effect", insn_ip);
285+
}
286+
287+
// Make sure we don't use more than the declared stack size.
288+
if(sp > max_stack_allowed_) {
289+
fail(state, "stack overflow", insn_ip);
290+
}
291+
292+
// Keep track of the max stack depth seen
293+
if(sp > max_stack_seen_) max_stack_seen_ = sp;
294+
295+
switch(op) {
296+
case InstructionSequence::insn_push_local:
297+
case InstructionSequence::insn_set_local:
298+
if((native_int)arg1 < 0 || (native_int)arg1 >= locals_) {
299+
fail(state, "invalid local variable access", insn_ip);
300+
}
301+
break;
302+
case InstructionSequence::insn_goto:
303+
if((native_int)arg1 < 0 || (native_int)arg1 >= total_) {
304+
fail(state, "invalid goto location", insn_ip);
305+
}
306+
307+
// Only handle forward branches.
308+
if((int)arg1 > ip) {
309+
ip = (int)arg1;
310+
} else {
311+
return;
312+
}
313+
break;
314+
case InstructionSequence::insn_push_stack_local:
315+
case InstructionSequence::insn_set_stack_local:
316+
if((int)arg1 > max_stack_local_) {
317+
max_stack_local_ = (int)arg1;
318+
}
319+
break;
320+
case InstructionSequence::insn_goto_if_false:
321+
case InstructionSequence::insn_goto_if_true:
322+
case InstructionSequence::insn_setup_unwind:
323+
if((native_int)arg1 < 0 || (native_int)arg1 >= total_) {
324+
fail(state, "invalid goto location", insn_ip);
325+
}
326+
327+
if((int)arg1 > ip) {
328+
ips.push_back(Section(sp, arg1));
329+
}
330+
331+
break;
332+
case InstructionSequence::insn_ret:
333+
case InstructionSequence::insn_raise_exc:
334+
case InstructionSequence::insn_raise_return:
335+
case InstructionSequence::insn_ensure_return:
336+
case InstructionSequence::insn_raise_break:
337+
case InstructionSequence::insn_reraise:
338+
return;
339+
}
340+
341+
// Detect falling off the end of the stream
342+
if(ip >= total_) {
343+
fail(state, "unterminated instruction sequence", insn_ip);
344+
}
345+
}
346+
}
347+
}

Diff for: ‎machine/bytecode_verifier.hpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#ifndef RBX_BYTECODE_VERIFICATION
2+
#define RBX_BYTECODE_VERIFICATION
3+
4+
#include <stdint.h>
5+
#include <list>
6+
7+
namespace rubinius {
8+
class CompiledCode;
9+
class Fixnum;
10+
class Object;
11+
class Tuple;
12+
class State;
13+
class VM;
14+
15+
class BytecodeVerifier {
16+
CompiledCode* method_;
17+
18+
Tuple* ops_;
19+
native_int total_;
20+
21+
int max_stack_allowed_;
22+
int max_stack_seen_;
23+
int32_t* stack_;
24+
25+
native_int locals_;
26+
int max_stack_local_;
27+
28+
struct Section {
29+
int sp;
30+
int ip;
31+
32+
Section(int sp, int ip)
33+
: sp(sp), ip(ip)
34+
{}
35+
};
36+
37+
public:
38+
BytecodeVerifier(CompiledCode* code);
39+
~BytecodeVerifier();
40+
41+
void verify(STATE);
42+
void verify_from(STATE, int sp, int ip, std::list<Section>& ips);
43+
44+
void verify_width(STATE, int ip);
45+
Fixnum* verify_opcode(STATE, int ip);
46+
Fixnum* verify_argument(STATE, int ip);
47+
Object* verify_object(STATE, int index, int ip);
48+
Symbol* verify_symbol(STATE, int index, int ip);
49+
Object* verify_symbol_or_nil(STATE, int index, int ip);
50+
Object* verify_code(STATE, int index, int ip);
51+
52+
NORETURN(void fail(STATE, const char* reason, int ip));
53+
};
54+
}
55+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.