-
Notifications
You must be signed in to change notification settings - Fork 605
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed BytecodeVerifier for CompiledCode#literals change.
- v5.0
- v4.20
- v4.19
- v4.18
- v4.17
- v4.16
- v4.15
- v4.14
- v4.13
- v4.12
- v4.11
- v4.10
- v4.9
- v4.8
- v4.7
- v4.6
- v4.5
- v4.4
- v4.3
- v4.2
- v4.1
- v4.0
- v3.107
- v3.106
- v3.105
- v3.104
- v3.103
- v3.102
- v3.101
- v3.100
- v3.99
- v3.98
- v3.97
- v3.96
- v3.95
- v3.94
- v3.93
- v3.92
- v3.91
- v3.90
- v3.89
- v3.88
- v3.87
- v3.86
- v3.85
- v3.84
- v3.83
- v3.82
- v3.81
- v3.80
- v3.79
- v3.78
- v3.77
- v3.76
- v3.75
- v3.74
- v3.73
- v3.72
- v3.71
- v3.70
- v3.69
- v3.68
- v3.67
- v3.66
- v3.65
- v3.64
- v3.63
- v3.62
- v3.61
- v3.60
- v3.59
- v3.58
- v3.57
- v3.56
- v3.55
- v3.54
- v3.53
- v3.52
- v3.51
- v3.50
- v3.49
- v3.48
- v3.47
- v3.46
- v3.45
- v3.44
- v3.43
- v3.42
- v3.41
- v3.40
- v3.39
- v3.38
- v3.37
- v3.36
- v3.35
- v3.34
- v3.33
- v3.32
- v3.31
- v3.30
- v3.29
- v3.28
- v3.27
- v3.26
Showing
7 changed files
with
409 additions
and
390 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,347 @@ | ||
#include "builtin/compiled_code.hpp" | ||
#include "builtin/exception.hpp" | ||
#include "builtin/fixnum.hpp" | ||
#include "builtin/iseq.hpp" | ||
#include "builtin/string.hpp" | ||
#include "builtin/symbol.hpp" | ||
#include "builtin/tuple.hpp" | ||
|
||
#include "bytecode_verifier.hpp" | ||
#include "configuration.hpp" | ||
#include "machine_code.hpp" | ||
#include "object_utils.hpp" | ||
|
||
#include "instruments/timing.hpp" | ||
|
||
namespace rubinius { | ||
BytecodeVerifier::BytecodeVerifier(CompiledCode* code) | ||
: method_(code) | ||
, ops_(NULL) | ||
, total_(0) | ||
, max_stack_allowed_(0) | ||
, max_stack_seen_(0) | ||
, stack_(0) | ||
, locals_(0) | ||
, max_stack_local_(-1) | ||
{} | ||
|
||
BytecodeVerifier::~BytecodeVerifier() { | ||
if(stack_) delete[] stack_; | ||
} | ||
|
||
void BytecodeVerifier::fail(STATE, const char* reason, int ip) { | ||
Exception::bytecode_error(state, method_, ip, reason); | ||
} | ||
|
||
void BytecodeVerifier::verify_width(STATE, int ip) { | ||
if(ip >= total_) { | ||
fail(state, "truncated instruction sequence", ip); | ||
} | ||
} | ||
|
||
Fixnum* BytecodeVerifier::verify_opcode(STATE, int ip) { | ||
verify_width(state, ip); | ||
|
||
if(Fixnum* fix = try_as<Fixnum>(ops_->at(ip))) { | ||
return fix; | ||
} else { | ||
fail(state, "instruction is not a Fixnum", ip); | ||
} | ||
} | ||
|
||
Fixnum* BytecodeVerifier::verify_argument(STATE, int ip) { | ||
verify_width(state, ip); | ||
|
||
if(Fixnum* fix = try_as<Fixnum>(ops_->at(ip))) { | ||
return fix; | ||
} else { | ||
fail(state, "instruction argument is not a Fixnum", ip); | ||
} | ||
} | ||
|
||
Object* BytecodeVerifier::verify_object(STATE, int index, int ip) { | ||
if(Object* obj = try_as<Object>(method_->literals()->at(index))) { | ||
return obj; | ||
} else { | ||
fail(state, "instruction argument is not an Object", ip); | ||
} | ||
} | ||
|
||
Symbol* BytecodeVerifier::verify_symbol(STATE, int index, int ip) { | ||
if(Symbol* sym = try_as<Symbol>(method_->literals()->at(index))) { | ||
return sym; | ||
} else { | ||
fail(state, "instruction argument is not a Symbol", ip); | ||
} | ||
} | ||
|
||
Object* BytecodeVerifier::verify_symbol_or_nil(STATE, int index, int ip) { | ||
Object* val = method_->literals()->at(index); | ||
|
||
if(val->nil_p()) { | ||
return val; | ||
} else if(Symbol* sym = try_as<Symbol>(val)) { | ||
return sym; | ||
} else { | ||
fail(state, "instruction argument is not a Symbol or nil", ip); | ||
} | ||
} | ||
|
||
Object* BytecodeVerifier::verify_code(STATE, int index, int ip) { | ||
if(CompiledCode* code = try_as<CompiledCode>(method_->literals()->at(index))) { | ||
return force_as<Object>(code); | ||
} else if(String* str = try_as<String>(method_->literals()->at(index))) { | ||
return force_as<Object>(str); | ||
} else { | ||
fail(state, "instruction argument is not a String or CompiledCode", ip); | ||
} | ||
} | ||
|
||
void BytecodeVerifier::verify(STATE) { | ||
// Do this setup here instead of the constructor so we can do | ||
// some validation of the CompiledCode's fields we read them. | ||
|
||
// Double check the method itself, since it might be a nil | ||
if(!kind_of<CompiledCode>(method_)) { | ||
fail(state, "not passed a CompiledCode object", -1); | ||
} | ||
|
||
if(Fixnum* fix = try_as<Fixnum>(method_->local_count())) { | ||
locals_ = fix->to_native(); | ||
} else { | ||
fail(state, "local_count is not a Fixnum", -1); | ||
} | ||
|
||
InstructionSequence* iseq = try_as<InstructionSequence>(method_->iseq()); | ||
if(!iseq) { | ||
fail(state, "iseq is not an InstructionSequence", -1); | ||
} | ||
|
||
if(Tuple* tup = try_as<Tuple>(iseq->opcodes())) { | ||
ops_ = tup; | ||
} else { | ||
fail(state, "opcodes is not a Tuple", -1); | ||
} | ||
|
||
if(Fixnum* fix = try_as<Fixnum>(method_->stack_size())) { | ||
max_stack_allowed_ = fix->to_native(); | ||
} else { | ||
fail(state, "stack_size is not a Fixnum", -1); | ||
} | ||
|
||
if(Fixnum* fix = try_as<Fixnum>(method_->splat())) { | ||
if(fix->to_native() >= locals_) { | ||
fail(state, "invalid splat position", -1); | ||
} | ||
} | ||
|
||
Fixnum* tot = try_as<Fixnum>(method_->total_args()); | ||
if(!tot) { | ||
fail(state, "total_args is not a Fixnum", -1); | ||
} | ||
Fixnum* req = try_as<Fixnum>(method_->required_args()); | ||
if(!req) { | ||
fail(state, "required_args is not a Fixnum", -1); | ||
} | ||
Fixnum* post = try_as<Fixnum>(method_->post_args()); | ||
if(!post) { | ||
fail(state, "post_args is not a Fixnum", -1); | ||
} | ||
|
||
if(tot->to_native() > locals_) { | ||
fail(state, "more arguments than local slots", -1); | ||
} | ||
|
||
if(req->to_native() > tot->to_native()) { | ||
fail(state, "more required arguments than total", -1); | ||
} | ||
|
||
if(post->to_native() > req->to_native()) { | ||
fail(state, "more post arguments than required", -1); | ||
} | ||
|
||
if(post->to_native() > tot->to_native()) { | ||
fail(state, "more post arguments than total", -1); | ||
} | ||
|
||
total_ = ops_->num_fields(); | ||
stack_ = new int32_t[total_]; | ||
|
||
for(native_int i = 0; i < total_; i++) { | ||
stack_[i] = -1; | ||
} | ||
|
||
std::list<Section> ips; | ||
ips.push_back(Section(0, 0)); | ||
|
||
while(!ips.empty()) { | ||
Section& section = ips.front(); | ||
|
||
int ip = section.ip; | ||
int sp = section.sp; | ||
|
||
ips.pop_front(); | ||
|
||
verify_from(state, sp, ip, ips); | ||
} | ||
|
||
// Now, check there is a enough space for the stack locals. | ||
if(max_stack_seen_ + max_stack_local_ >= max_stack_allowed_) { | ||
fail(state, "not enough space for stack locals", -1); | ||
} | ||
} | ||
|
||
namespace { | ||
#include "gen/instruction_effects.hpp" | ||
} | ||
|
||
void BytecodeVerifier::verify_from(STATE, int sp, int ip, std::list<Section>& ips) { | ||
int insn_ip = ip; | ||
|
||
if(sp < 0) { | ||
fail(state, "stack underflow error", ip); | ||
} | ||
|
||
for(;;) { | ||
int old_sp = stack_[ip]; | ||
if(old_sp < 0) { | ||
stack_[ip] = sp; | ||
} else if(old_sp != sp) { | ||
fail(state, "inconsistent stack depth", ip); | ||
} else { | ||
// Already been here and stack is consistent, done. | ||
return; | ||
} | ||
|
||
opcode op = static_cast<opcode>(verify_opcode(state, ip++)->to_native()); | ||
size_t width = InstructionSequence::instruction_width(op); | ||
|
||
opcode arg1 = 0, arg2 = 0, arg3 = 0; | ||
|
||
switch(width) { | ||
case 1: | ||
break; | ||
case 2: | ||
arg1 = static_cast<opcode>(verify_argument(state, ip++)->to_native()); | ||
break; | ||
case 3: | ||
arg1 = static_cast<opcode>(verify_argument(state, ip++)->to_native()); | ||
arg2 = static_cast<opcode>(verify_argument(state, ip++)->to_native()); | ||
break; | ||
case 4: | ||
arg1 = static_cast<opcode>(verify_argument(state, ip++)->to_native()); | ||
arg2 = static_cast<opcode>(verify_argument(state, ip++)->to_native()); | ||
arg3 = static_cast<opcode>(verify_argument(state, ip++)->to_native()); | ||
break; | ||
default: | ||
fail(state, "invalid instruction", insn_ip); | ||
} | ||
|
||
switch(op) { | ||
case InstructionSequence::insn_push_literal: | ||
case InstructionSequence::insn_push_memo: | ||
verify_object(state, arg1, insn_ip); | ||
break; | ||
case InstructionSequence::insn_create_block: | ||
verify_code(state, arg1, insn_ip); | ||
break; | ||
case InstructionSequence::insn_send_vcall: | ||
case InstructionSequence::insn_send_method: | ||
case InstructionSequence::insn_send_stack: | ||
case InstructionSequence::insn_send_stack_with_block: | ||
case InstructionSequence::insn_send_stack_with_splat: | ||
case InstructionSequence::insn_check_serial: | ||
case InstructionSequence::insn_check_serial_private: | ||
case InstructionSequence::insn_invoke_primitive: | ||
case InstructionSequence::insn_set_ivar: | ||
case InstructionSequence::insn_push_ivar: | ||
case InstructionSequence::insn_set_const: | ||
case InstructionSequence::insn_set_const_at: | ||
case InstructionSequence::insn_object_to_s: | ||
case InstructionSequence::insn_push_const: | ||
case InstructionSequence::insn_find_const: | ||
case InstructionSequence::insn_zsuper: | ||
verify_symbol(state, arg1, insn_ip); | ||
break; | ||
case InstructionSequence::insn_send_super_stack_with_block: | ||
case InstructionSequence::insn_send_super_stack_with_splat: | ||
verify_symbol_or_nil(state, arg1, insn_ip); | ||
break; | ||
} | ||
|
||
int read = 0, write = 0; | ||
|
||
int effect = stack_difference(op, arg1, arg2, arg3, &read, &write); | ||
|
||
// Check for under read | ||
if(sp - read < 0) { | ||
fail(state, "stack underflow on read", insn_ip); | ||
} | ||
|
||
// Apply the total effect to propagate it. | ||
sp += effect; | ||
if(sp < 0) { | ||
fail(state, "stack underflow on effect", insn_ip); | ||
} | ||
|
||
// Make sure we don't use more than the declared stack size. | ||
if(sp > max_stack_allowed_) { | ||
fail(state, "stack overflow", insn_ip); | ||
} | ||
|
||
// Keep track of the max stack depth seen | ||
if(sp > max_stack_seen_) max_stack_seen_ = sp; | ||
|
||
switch(op) { | ||
case InstructionSequence::insn_push_local: | ||
case InstructionSequence::insn_set_local: | ||
if((native_int)arg1 < 0 || (native_int)arg1 >= locals_) { | ||
fail(state, "invalid local variable access", insn_ip); | ||
} | ||
break; | ||
case InstructionSequence::insn_goto: | ||
if((native_int)arg1 < 0 || (native_int)arg1 >= total_) { | ||
fail(state, "invalid goto location", insn_ip); | ||
} | ||
|
||
// Only handle forward branches. | ||
if((int)arg1 > ip) { | ||
ip = (int)arg1; | ||
} else { | ||
return; | ||
} | ||
break; | ||
case InstructionSequence::insn_push_stack_local: | ||
case InstructionSequence::insn_set_stack_local: | ||
if((int)arg1 > max_stack_local_) { | ||
max_stack_local_ = (int)arg1; | ||
} | ||
break; | ||
case InstructionSequence::insn_goto_if_false: | ||
case InstructionSequence::insn_goto_if_true: | ||
case InstructionSequence::insn_setup_unwind: | ||
if((native_int)arg1 < 0 || (native_int)arg1 >= total_) { | ||
fail(state, "invalid goto location", insn_ip); | ||
} | ||
|
||
if((int)arg1 > ip) { | ||
ips.push_back(Section(sp, arg1)); | ||
} | ||
|
||
break; | ||
case InstructionSequence::insn_ret: | ||
case InstructionSequence::insn_raise_exc: | ||
case InstructionSequence::insn_raise_return: | ||
case InstructionSequence::insn_ensure_return: | ||
case InstructionSequence::insn_raise_break: | ||
case InstructionSequence::insn_reraise: | ||
return; | ||
} | ||
|
||
// Detect falling off the end of the stream | ||
if(ip >= total_) { | ||
fail(state, "unterminated instruction sequence", insn_ip); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#ifndef RBX_BYTECODE_VERIFICATION | ||
#define RBX_BYTECODE_VERIFICATION | ||
|
||
#include <stdint.h> | ||
#include <list> | ||
|
||
namespace rubinius { | ||
class CompiledCode; | ||
class Fixnum; | ||
class Object; | ||
class Tuple; | ||
class State; | ||
class VM; | ||
|
||
class BytecodeVerifier { | ||
CompiledCode* method_; | ||
|
||
Tuple* ops_; | ||
native_int total_; | ||
|
||
int max_stack_allowed_; | ||
int max_stack_seen_; | ||
int32_t* stack_; | ||
|
||
native_int locals_; | ||
int max_stack_local_; | ||
|
||
struct Section { | ||
int sp; | ||
int ip; | ||
|
||
Section(int sp, int ip) | ||
: sp(sp), ip(ip) | ||
{} | ||
}; | ||
|
||
public: | ||
BytecodeVerifier(CompiledCode* code); | ||
~BytecodeVerifier(); | ||
|
||
void verify(STATE); | ||
void verify_from(STATE, int sp, int ip, std::list<Section>& ips); | ||
|
||
void verify_width(STATE, int ip); | ||
Fixnum* verify_opcode(STATE, int ip); | ||
Fixnum* verify_argument(STATE, int ip); | ||
Object* verify_object(STATE, int index, int ip); | ||
Symbol* verify_symbol(STATE, int index, int ip); | ||
Object* verify_symbol_or_nil(STATE, int index, int ip); | ||
Object* verify_code(STATE, int index, int ip); | ||
|
||
NORETURN(void fail(STATE, const char* reason, int ip)); | ||
}; | ||
} | ||
#endif |