提交 da76cddc 编写于 作者: M Matt Witherspoon

Make all wasm opcodes have an explicit central disposition

With the talk of supporting more than one wasm execution environment, make the decision on allowable opcodes in a central location which enforces white/black listing for each opcode. The hope here is that in a single file it is possible to review all opcodes, their constraints, and if they are blacklisted, etc

eosiod will refuse to run if an opcode is not dispositioned by some manner. Disappointedly, I had to do some copy pasta on most of the whitelisted opcodes due to current design. However, due to all methods in eosio_constraints_visitor being marked override, there is not much chance of an error here so I'd like to roll with this still.
上级 7cea53dc
......@@ -17,4 +17,7 @@ namespace wasm_constraints {
//Throws if something in the module violates
void validate_eosio_wasm_constraints(const IR::Module& m);
//Throws if an opcode is neither whitelisted nor blacklisted
void check_wasm_opcode_dispositions();
}}
......@@ -8,11 +8,15 @@ namespace eosio { namespace chain {
using namespace IR;
struct wasm_opcode_no_disposition_exception {
string opcode_name;
};
struct nop_opcode_visitor {
typedef void Result;
#define VISIT_OPCODE(opcode,name,nameString,Imm,...) \
virtual void name(Imm) {}
virtual void name(Imm) {throw wasm_opcode_no_disposition_exception{nameString}; }
ENUM_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
......@@ -22,9 +26,6 @@ struct nop_opcode_visitor {
};
struct eosio_constraints_visitor : public nop_opcode_visitor {
///Make this some sort of visitor enum to reduce chance of copy pasta errors (but
// the override declaration makes it somewhat safe)
//While it's possible to access beyond 1MiB by giving an offset that's 1MiB-1 and
// an 8 byte data type, that's fine. There will be enough of a guard on the end
// of 1MiB where it's not a problem
......@@ -52,18 +53,128 @@ struct eosio_constraints_visitor : public nop_opcode_visitor {
void i64_store16 (LoadOrStoreImm<1> imm) override { fail_large_offset(imm.offset); }
void i64_store32 (LoadOrStoreImm<2> imm) override { fail_large_offset(imm.offset); }
#if 0 //These are caught by ENUM_FLOAT_NONCONTROL_NONPARAMETRIC_OPERATORS below, but should be placed back when float is supported
void f32_load (LoadOrStoreImm<2> imm) override { fail_large_offset(imm.offset); }
void f64_load (LoadOrStoreImm<3> imm) override { fail_large_offset(imm.offset); }
void f32_store (LoadOrStoreImm<2> imm) override { fail_large_offset(imm.offset); }
void f64_store (LoadOrStoreImm<3> imm) override { fail_large_offset(imm.offset); }
#endif
#define VISIT_OPCODE(opcode,name,nameString,Imm,...) \
void name(Imm) override { FC_THROW_EXCEPTION(wasm_execution_error, "Smart contracts may not use WASM memory operators"); }
ENUM_MEMORY_OPERATORS(VISIT_OPCODE);
#undef VISIT_OPCODE
#define VISIT_OPCODE(opcode,name,nameString,Imm,...) \
void name(Imm) override { FC_THROW_EXCEPTION(wasm_execution_error, "Smart contracts may not use any floating point opcodes"); }
ENUM_FLOAT_NONCONTROL_NONPARAMETRIC_OPERATORS(VISIT_OPCODE);
#undef VISIT_OPCODE
//Allow all these through untouched/////////////////////////
#define VISIT_OPCODE(opcode,name,nameString,Imm,...) \
void name(Imm) override {}
ENUM_CONTROL_OPERATORS(VISIT_OPCODE);
ENUM_PARAMETRIC_OPERATORS(VISIT_OPCODE);
//annoyingly the rest need to be defined manually given current design
VISIT_OPCODE(0x01,nop,"nop",NoImm,NULLARY(none));
VISIT_OPCODE(0x41,i32_const,"i32.const",LiteralImm<I32>,NULLARY(i32));
VISIT_OPCODE(0x42,i64_const,"i64.const",LiteralImm<I64>,NULLARY(i64));
VISIT_OPCODE(0x45,i32_eqz,"i32.eqz",NoImm,UNARY(i32,i32));
VISIT_OPCODE(0x46,i32_eq,"i32.eq",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x47,i32_ne,"i32.ne",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x48,i32_lt_s,"i32.lt_s",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x49,i32_lt_u,"i32.lt_u",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x4a,i32_gt_s,"i32.gt_s",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x4b,i32_gt_u,"i32.gt_u",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x4c,i32_le_s,"i32.le_s",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x4d,i32_le_u,"i32.le_u",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x4e,i32_ge_s,"i32.ge_s",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x4f,i32_ge_u,"i32.ge_u",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x50,i64_eqz,"i64.eqz",NoImm,UNARY(i64,i32));
VISIT_OPCODE(0x51,i64_eq,"i64.eq",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x52,i64_ne,"i64.ne",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x53,i64_lt_s,"i64.lt_s",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x54,i64_lt_u,"i64.lt_u",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x55,i64_gt_s,"i64.gt_s",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x56,i64_gt_u,"i64.gt_u",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x57,i64_le_s,"i64.le_s",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x58,i64_le_u,"i64.le_u",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x59,i64_ge_s,"i64.ge_s",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x5a,i64_ge_u,"i64.ge_u",NoImm,BINARY(i64,i32));
VISIT_OPCODE(0x67,i32_clz,"i32.clz",NoImm,UNARY(i32,i32));
VISIT_OPCODE(0x68,i32_ctz,"i32.ctz",NoImm,UNARY(i32,i32));
VISIT_OPCODE(0x69,i32_popcnt,"i32.popcnt",NoImm,UNARY(i32,i32));
VISIT_OPCODE(0x6a,i32_add,"i32.add",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x6b,i32_sub,"i32.sub",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x6c,i32_mul,"i32.mul",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x6d,i32_div_s,"i32.div_s",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x6e,i32_div_u,"i32.div_u",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x6f,i32_rem_s,"i32.rem_s",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x70,i32_rem_u,"i32.rem_u",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x71,i32_and,"i32.and",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x72,i32_or,"i32.or",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x73,i32_xor,"i32.xor",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x74,i32_shl,"i32.shl",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x75,i32_shr_s,"i32.shr_s",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x76,i32_shr_u,"i32.shr_u",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x77,i32_rotl,"i32.rotl",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x78,i32_rotr,"i32.rotr",NoImm,BINARY(i32,i32));
VISIT_OPCODE(0x79,i64_clz,"i64.clz",NoImm,UNARY(i64,i64));
VISIT_OPCODE(0x7a,i64_ctz,"i64.ctz",NoImm,UNARY(i64,i64));
VISIT_OPCODE(0x7b,i64_popcnt,"i64.popcnt",NoImm,UNARY(i64,i64));
VISIT_OPCODE(0x7c,i64_add,"i64.add",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x7d,i64_sub,"i64.sub",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x7e,i64_mul,"i64.mul",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x7f,i64_div_s,"i64.div_s",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x80,i64_div_u,"i64.div_u",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x81,i64_rem_s,"i64.rem_s",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x82,i64_rem_u,"i64.rem_u",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x83,i64_and,"i64.and",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x84,i64_or,"i64.or",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x85,i64_xor,"i64.xor",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x86,i64_shl,"i64.shl",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x87,i64_shr_s,"i64.shr_s",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x88,i64_shr_u,"i64.shr_u",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x89,i64_rotl,"i64.rotl",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0x8a,i64_rotr,"i64.rotr",NoImm,BINARY(i64,i64));
VISIT_OPCODE(0xa7,i32_wrap_i64,"i32.wrap/i64",NoImm,UNARY(i64,i32));
VISIT_OPCODE(0xac,i64_extend_s_i32,"i64.extend_s/i32",NoImm,UNARY(i32,i64));
VISIT_OPCODE(0xad,i64_extend_u_i32,"i64.extend_u/i32",NoImm,UNARY(i32,i64));
#undef VISIT_OPCODE
};
void check_wasm_opcode_dispositions() {
eosio_constraints_visitor visitor;
vector<string> opcodes_without_disposition;
vector<string> opcodes_allowed;
#define VISIT_OPCODE(opcode,name,nameString,Imm,...) \
try { \
visitor.name(Imm{}); \
opcodes_allowed.push_back(nameString); \
} \
catch(wasm_execution_error& e) {} \
catch(wasm_opcode_no_disposition_exception& e) { \
opcodes_without_disposition.push_back(e.opcode_name); \
}
ENUM_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
if(opcodes_without_disposition.size()) {
elog("WASM opcode disposition not defined for opcodes:");
for(const string& g : opcodes_without_disposition)
elog(" ${p}", ("p", g));
FC_THROW("All opcodes must have a constraint");
}
}
void validate_eosio_wasm_constraints(const Module& m) {
if(m.memories.defs.size() && m.memories.defs[0].type.size.min > wasm_constraints::maximum_linear_memory/(64*1024))
FC_THROW_EXCEPTION(wasm_execution_error, "Smart contract initial memory size must be less than or equal to ${k}KiB", ("k", wasm_constraints::maximum_linear_memory/1024));
......@@ -97,6 +208,7 @@ void validate_eosio_wasm_constraints(const Module& m) {
if(mutable_globals_total_size > wasm_constraints::maximum_mutable_globals)
FC_THROW_EXCEPTION(wasm_execution_error, "Smart contract has more than ${k} bytes of mutable globals", ("k", wasm_constraints::maximum_mutable_globals));
//Now check for constraints on each opcode.
//Some of the OperatorDecoderStream users inside of WAVM track the control stack and quit parsing from
// OperatorDecoderStream when the control stack is empty (since that would indicate unreachable code).
// Not doing that here, yet, since it's not clear it's required for the purpose of the validation
......
......@@ -98,6 +98,7 @@ namespace eosio { namespace chain {
struct wasm_cache_impl {
wasm_cache_impl()
{
check_wasm_opcode_dispositions();
Runtime::init();
}
......
......@@ -629,7 +629,6 @@ namespace WASM
{
Opcode opcode;
serialize(bodyStream,opcode);
switch(opcode)
{
#define VISIT_OPCODE(_,name,nameString,Imm,...) \
......@@ -643,16 +642,8 @@ namespace WASM
injection.conditionallyAddCall(opcode, imm, module, irEncoderStream, codeValidationStream); \
break; \
}
ENUM_NONFLOAT_OPERATORS(VISIT_OPCODE)
ENUM_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
/////disallow float operations
#define VISIT_OPCODE(_,name,nameString,...) \
case Opcode::name: \
throw FatalSerializationException("float instructions not allowed");
ENUM_FLOAT_NONCONTROL_NONPARAMETRIC_OPERATORS(VISIT_OPCODE)
#undef VISIT_OPCODE
default: throw FatalSerializationException("unknown opcode");
};
};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册